Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Section ordering change was not backwards compatible #136

Closed
Sanqui opened this issue Mar 14, 2017 · 31 comments
Closed

Section ordering change was not backwards compatible #136

Sanqui opened this issue Mar 14, 2017 · 31 comments

Comments

@Sanqui
Copy link
Member

Sanqui commented Mar 14, 2017

In 1b05c43, section ordering was changed:

This version of the linker will allocate sections by their alignment, and then by their size (largest first, in both cases).

I am assuming the old algorithm allocated sections linearly in the order they appear in the source.

This breaks both https://github.com/pret/pokered and https://github.com/pret/pokecrystal (issue pret/pokecrystal#356) as they relied on the previous behavior. (The ROMs built no longer match the originals.)

In my opinion, the old behavior should be brought back and the new behavior should be placed under a flag.

Thanks to @FredrIQ (and @aphirst) for noticing.

@AntonioND
Copy link
Member

CC: @Ben10do

I'm not sure about this.

First of all, I'm aware of this change in behaviour, I tested the PR on my game (µCity) and realized that the binary was indeed different. I also made sure that it worked as expected, so I merged it because it didn't break anything. My game uses enough things of rgbds to be useful as a test rom, and I use floating sections for almost everything, so the fact that it worked seemed to me like a good enough reason to accept the PR.

It's true that it's not 'backwards compatible', according to some definition of 'backwards compatibility'. It's also true that the real problem is that both repositories rely on something extremely fragile and undocumented, which was due to change at some point anyway because the previous system was suboptimal, as said in issue #83. This is not a real compatibility break, it's just that you relied on something that happened by accident. If you really depend on a specific order of your sections you should be using fixed sections (by both bank and address).

While I don't think that the current system is perfect, it's closer to perfection than the previous one. First, you place the big sections and then the small ones to fill any holes left behind. It seems like a more sensible way of placing sections. It's like the 'rocks, pebbles and sand' story: if you first add all your sand to a jar you won't have space to add the pebbles and the rocks. If you happen to add first all your small sections you won't have space for the big ones even though they could perfectly fit if you reorganized them.

Also, I'm not sure if adding a flag to the linker to make it dumber makes sense. This is not like the optimization level of GCC, the code is still the same, the sections are still placed in valid ways, and anybody using rgbds as it was designed to be used shouldn't have any issues.

@AntonioND
Copy link
Member

AntonioND commented Mar 14, 2017

That being said, I don't know what your problem is. As far as I can see you are fixing all sections to banks.

Maybe floating WRAM sections?

Try to fix the following sections of pokered and check if it works:

wram.asm:63:SECTION "WRAM Bank 0", WRAM0
home.asm:33:SECTION "Home", ROM0
home.asm:102:SECTION "Main", ROM0

This goes for pokecrystal:

lib/mobile/main.asm:7:SECTION "Main", ROMX
wram.asm:5:SECTION "Stack", WRAM0
wram.asm:14:SECTION "Audio", WRAM0
wram.asm:137:SECTION "WRAM", WRAM0
wram.asm:333:SECTION "Tilemap", WRAM0
wram.asm:341:SECTION "Battle", WRAM0
wram.asm:1114:SECTION "Video", WRAM0

@AntonioND
Copy link
Member

I've fixed pokered (I think).

pret/pokered#144

@BenHetherington
Copy link
Contributor

Of course this would blow up when I'm afk 😝

I don't have too much to add to what @AntonioND said, although I definitely agree that adding an extra flag to support the old behaviour would be silly – maintaining two section assignment algorithms feels a lot like overkill to me!

A slightly more in-depth explanation of the changes, if you're curious:
Floating ROMX, VRAM, WRAMX, and SRAM sections should not have been affected – they've always allocated the biggest sections first, or at least long before my change. However, floating ROM0, WRAM0, and HRAM sections were, indeed, previously assigned in order. This inconsistency didn't really make sense then, but when I came around to implementing byte alignment, I realised that the second method certainly wouldn't have worked well, which mandated that I reuse the above behaviour.

Regardless, sorry to see that these changes has caused a blip for these disassemblies, and I'm happy to look into fixing pokecrystal (if @AntonioND or someone else hasn't already done so by the time I get there).

@AntonioND
Copy link
Member

AntonioND commented Mar 14, 2017

I've created a PR for pokered (well, fixed the one I've referenced before). I'm doing the same for pokecrystal.

@BenHetherington
Copy link
Contributor

I've just submitted a PR for pokecrystal (pret/pokecrystal#357), hope this makes up for the trouble.

@AntonioND
Copy link
Member

Oh, great, I was doing it at the same time and didn't read your message... pret/pokecrystal#358

@AntonioND
Copy link
Member

In any case, this issue is not going to be fixed. Any other dissasembly affected by this will have to fix the affected source code.

@PikalaxALT
Copy link
Contributor

An unsatisfactory conclusion to this discussion, as projects derived from disassemblies are more severely affected.

@AntonioND
Copy link
Member

Sorry, we cannot guarantee that the linker will always have the same behaviour because that limits future optimizations and it wasn't documented anywhere that it was supposed to behave like that. A flag to go back to the previous dumb behaviour would be more code to maintain.

It is not the duty of the maintainers of this repository to make sure that incorrect code works as expected.

In short: If you need a fixed section, use a fixed section, not a floating section.

The PRs created in this discussion are a nice gesture to the maintainers of the disassemblies referred by @Sanqui but we had no obligation to do them.

@AntonioND
Copy link
Member

https://xkcd.com/1172/

@aaaaaa123456789
Copy link
Member

In short: If you need a fixed section, use a fixed section, not a floating section.

And how would we specify "we want section B to come immediately after section A, regardless of how big they are"? Under the old behavior, this was straightforward — place them in order. That's impossible now, and making them fixed in order to be consecutive requires knowing their exact size.

The old behavior made this simple. Write the sections in the order they should be, and that's it. While you may think "you can just merge them into a single section", this is not possible when multiple object files are involved, and purely impractical when the codebase is hundreds of thousands of lines long.

On the other hand, there is no solution to this problem now. Fixed sections require knowing the exact size of the section that comes first, which has an obvious problem that makes the solution unusable: any single change to the section (don't think about disassemblies, think about modified versions) requires updating the address for the second section. Which in turn requires tracking where that second section is.

Don't get me wrong; I understand that there are downsides to requiring sections to appear in the order they are written. But those downsides are often made up for by the allocation simplicity that this model has — if you need sections to come up in a specific order, removing this feature makes this impossible, to the point sections as a whole become unusable for the project.

@BenHetherington
Copy link
Contributor

As I said, this didn't even consistently happen under the old allocation algorithm; the allocation order would differ depending on the section type and whether the section was bank-fixed.

@AntonioND
Copy link
Member

The old behavior made this simple. Write the sections in the order they should be, and that's it. While you may think "you can just merge them into a single section", this is not possible when multiple object files are involved, and purely impractical when the codebase is hundreds of thousands of lines long.

It wasn't simple, you needed to specify the order of everything, including the object files. If your Makefile or build script was a bit clever and searched for object files automatically even a simple change of name could change the order. Even splitting a file into two could break things if you aren't careful. It's not something you should rely on.

On the other hand, there is no solution to this problem now. Fixed sections require knowing the exact size of the section that comes first, which has an obvious problem that makes the solution unusable: any single change to the section (don't think about disassemblies, think about modified versions) requires updating the address for the second section. Which in turn requires tracking where that second section is.

Sure, that's a problem, and the only thing I can say is to use the same section for everything that has to be contiguous.

I've checked the source code of a few disassemblies, and they are a pretty good example of how not to do things (organization-wise).

If you really need something to be together you should put it in a single section. If you have different files with code that should go together, include the code of each one of them in another. Just create sections called "bank 40" or whatever and include everything that should be there in the correct order.

And I'm sorry that most disassemblies are affected by this, but this wouldn't have happened if they hadn't relied on this behaviour. That's simply a hack, the code is plain wrong. If you need things to be placed in a specific place you shouldn't tell the linker that it's free to place them wherever it wants by using floating sections. If you need a specific order you need to tell the linker to do it explicitly. Floating sections allow the linker to do whatever it feels like doing, which is perfectly fine if you are writing something new and you only care about using the space in an optimal way.

And yes, for pure disassemblies this is less of a problem than for modified versions, for modified versions you need to modify the sizes. But still, if you create sections that represent banks you can add all the code of all involved files and be sure that it will always be together. That's what should have been done from the start to make sure that the behaviour was consistent.

I'm really sorry for disassemblies, but this is their fault. I suggest they use commit 7b8d4de of rgbds until they fix their codebase.

And I repeat, this was going to happen at some point but it happens that 'at some point' has been earlier than expected: #82 #83

@AntonioND
Copy link
Member

Funnily enough, there's a really old issue in pret/pokered wondering what would happen if the linker changed its behaviour: pret/pokered#77

@aaaaaa123456789
Copy link
Member

So, here we are with this issue. There are basically three use cases with section placement:

  1. You want some sections to be in a specific place
  2. You want some sections to be anywhere, perhaps fixed to a certain bank
  3. You want some sections to be anywhere (as above), but in a specific order, without gaps

Case 2 requires no effort, so there's nothing to address. Case 1 is a fixed section.

The issue here is case 3, since the new linker behavior makes this impossible. You say essentially that a floating section is case 2. That's fine, but how would a user do case 3 then?

@aaaaaa123456789
Copy link
Member

I'd edit my previous message, but mobile GitHub won't let me.

For the record, I'm not saying that case 2 sections need to be treated as case 3. I'm simply saying that there is a true need for case 3 sections. This would easily be fulfilled if they had their own syntax, backwards incompatible or not — if, say, SECTION "Foo", ROMX[], BANK[$46] meant "place this section immediately after the previous bank $46 section", then the issue would be no longer.

(The syntax is just an example; I'm not advocating for any specific syntax here.)

@AntonioND
Copy link
Member

AntonioND commented Mar 15, 2017

The problem is that 3 has never been supported, you were just abusing the behaviour of the linker for some types of sections.

Regarding the possibility of continuing sections, as in pret/pokered#77.

What is "the section placed immediately before"? Maybe you are just feeding the linker a bunch of object files, which one is the first one? What if you sort them alphabetically before feeding them to the linker, one of them changes the name and the order is different? What if you define a second section in a bank and then want to continue the first one? Maybe joining sections with the same name could be a fix, but it is a hacky fix (or it seems to me quite hacky, right now it is useful to disallow that to detect mistakes). There are more problems. For example, relative jumps are only allowed within sections, but the continuation system wouldn't let you use them between different parts of sections.

What you are proposing for 3, after taking a look at the disassemblies, should be done like this with the current linker:

SECTION "Bank 40", ROMX[$4000], BANK[40]
INCLUDE "file1_b40.asm"
INCLUDE "file2_b40.asm"
INCLUDE "file3_b40.asm"

SECTION "Bank 41", ROMX[$4000], BANK[41]
INCLUDE "file1_b41.asm"
INCLUDE "file2_b41.asm"
INCLUDE "file3_b41.asm"

The alternative is to allow to continue sections, which is problematic as explained above.

@aaaaaa123456789
Copy link
Member

aaaaaa123456789 commented Mar 15, 2017 via email

@AntonioND
Copy link
Member

With the current linker, if 2 chunks of code need to be together they belong in the same section, there's no discussion there. If you don't want to recompile everything all the time you can have different sections for different banks in different object files and then link them together at the end. But all the code that needs to be together needs to be compiled in the same section.

Something like "banks_10_to_40.asm" and have all the code of those banks there.

Now, regarding 3.

I think that the ALIGN keyword was something long overdue, and the change of behaviour to fill holes was necessary. That's not going to change: this toolchain is not only used by disassemblers but by developers, and this is something that developers (like me) needed a long time ago.

If you disassemblers needed that extra functionality, you could have requested it instead of misusing the current behaviour. I'm not going back to the previous one because that sets the precedent that the toolchain needs to support whatever crazy tricks are being done by anyone. I know that in this case there's a lot of people affected. That's because they all copied the same flawed system, and that's not the fault of the new section placement code.

If there is any proposal for how to do 3, perfect. But it has to be consistent. "Continue the section before this one" is not a consistent behaviour because it depends on too many factors. It's not just the order of the sections inside an object file, but the order of the object files when they are linked together. That's simply not good enough.

@aaaaaa123456789
Copy link
Member

aaaaaa123456789 commented Mar 15, 2017 via email

@AntonioND
Copy link
Member

AntonioND commented Mar 15, 2017

First of all. This is not a regular game. You are trying to assemble code into a reference ROM. That is good enough reason to use a code similar to what I wrote before.

I repeat, what you are proposing is simply not supported and it was never supported.

That being said, something like this could be implemented:

SECTION "1A", ROMX, BANK[1]
label1: DB 1
SECTION "1B", CONTINUES "1A"
label2: DB 2

Not sure about the interactions with ALIGN.

@Sanqui
Copy link
Member Author

Sanqui commented Mar 15, 2017

Finally getting around checking on this.

I agree that the old behavior shouldn't have been relied on in the first place, and I didn't realize it was inconsistent between section types either. Despite that, backwards compatibility would have been nice. I will respect the maintainer's choice however.

I want to extend a thank you to @AntonioND and @Ben10do for stepping forward and voluntarily correcting the issues in the pokered and pokecrystal repos.

@aaaaaa123456789
Copy link
Member

aaaaaa123456789 commented Mar 15, 2017

Reference ROMs are modified, and it in the spirit of allowing and simplifying those modifications that I'm making these suggestions.

At this point I have to ask, how about a manifest file? A separate file listing sections and their intended locations, such as:

ROMX[1]
  section "1A", $4000
  section "1B" ; immediately after, since nothing is specified
ROMX[2]
  section "2A", $4000
  section "2B"

Of course, any format is acceptable.

EDIT: needless to say, the linker would be free to place unmentioned floating sections anywhere. That's all of them if no manifest is used.

@AntonioND
Copy link
Member

AntonioND commented Mar 15, 2017

That would involve big changes in the linker, starting with a parser of the manifiest file, which would only be useful for this corner case. A CONTINUES keyword would basically join two sections inside the linker, making it really easy to implement (we would only need to modify a bit the parser of the assembler and the format of the object file).

I'll give others a chance to say what they think it's a better option, anyway. New ideas are welcome.

@aaaaaa123456789
Copy link
Member

aaaaaa123456789 commented Mar 15, 2017 via email

@BenHetherington
Copy link
Contributor

Both sound like valid approaches to me; while parsing the manifest file would be more work, it sounds quite a bit more structured to me - perhaps this would be a great thing to add if we do rewrite the linker. Equally, a CONTINUES keyword would be easy to implement for the time being, although I see @aaaaaa123456789's point.

Also, it'd probably be a good idea to create a new issue for the feature request (including these proposed solutions), and leave this bug report closed.

@aaaaaa123456789
Copy link
Member

I'll be happy to help with the implementation if you need a hand, although I'm hardly available during the week nowadays. Weekends should be fine.

Do you want to make the feature request issue yourself, or should we make it?

@BenHetherington
Copy link
Contributor

I really don't mind; if you're happy to make it, you seem to have the best grasp of the reasons why this is needed 😉

@AntonioND
Copy link
Member

The problem with the manifiest is that it is essentially creating a parallel system of linking sections. What happens if you specify 2 different placements for a region in both the source code and the manifiest? That cannot happen with the CONTINUE keyword.

I'm generally in favour of small changes, this one seems to be a really big change for this use case. And I'm specially against adding new parsers because the one of rgbasm is broken enough for the whole codebase.

But yeah, let's discuss this in a new issue.

@aaaaaa123456789
Copy link
Member

Done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants