@All And Max find out why we do crashes with .so linking : that was because by default we put .rodata into not writable segment, while in adtools version it is. Once we make .rodata be in writable segments, then .so linking works and no more crashes.
Question is: why .rodata neesd to be writable segment, to make .sobjs working ? Any reasons for ?
chars are defined as read only, can be trying to modify a string? is there a example of this behavior? Where does it actually crash?
Can we dump what variable that was defined in the .rodata and what variable is defined writeable area. Did someone wrongfully define something as const?
can it be write protecting it before initializing classes, perhaps.
(NutsAboutAmiga)
Basilisk II for AmigaOS4 AmigaInputAnywhere Excalibur and other tools and apps.
But nothing that indicates that data from .rodata section is target. So i think that the current elf.library makes something which want to modify data in the .rodata section or the associated segemnt with it.
See, we now have .rodata in 03, which is marked as "RW" , and we have binary working.
I do not know currently why we crash exactly , it can be:
1). because we have .rodata in RE (while original adtools one is just R) 2). or because we have .rodata in the same place where .text , .plt and co , and just the fact that .plt is not the end of segment.
Currently, we simple "fix" it , but putting .rodata inside of writable area together with .ctors, .dtors and stuff , but i am sure it's not very correct. Because it should be Read Only, so being marked as R (as it in original adtools), and not RE or RW .
OK, maybe the cause is that the .rodata is even marked as Executable, and that causes the crash, and moving .rodata being writable has the side effect of getting rid of the E flag.
Thus the Writebale flag is not really needed. But it has the side effect of getting rid of the Executable flag. Which than don't led to an crash.
Best solution should be to have .rodata only flagged as Readable without any other flags. Which will results in that .rodata gets its own segment. Thus looking more like the old binutils.
@Futaura At least with RW on .rodata it works and didnt crash, same as if it just R, but crash if we RE, i.e. marked as execute. Will check elf output before crash.
Here it is crashing when calling the .ctors functions. I've double-checked all this by disassembling and peeking at the crashed code in memory and it seems .ctors is being relocated correctly. Although the crashlog might not always indicate it, it looks like it is the puts@plt call which crashes, because that jumps to the wrong address due to the reloc not having been applied. R_PPC_PLTREL24 relocs are always ignored by elf.library (there's a note about it in the relnotes).
I'm not up to speed with shared objects, so my question is where should the jump offset be corrected? Is this something that elf.library should be doing or is the wrong code being generated by the compiler?
Certainly, I've not seen any crashes that indicate that .rodata is being written to, so it is probably related to the physical position of .rodata - relocs will be different then too.
(apologies for the heavy re-edit - hopefully, nobody read the first version )
Edited by Futaura on 2023/8/30 17:22:40 Edited by Futaura on 2023/8/30 17:24:47
The elf.library relnotes in question appear to indicate that there may have been special modifications to binutils to make the PLT work:
- R_PPC_PLTREL24 relocs are now ignored for version 2 shared objects, since the jumps are already correctly targeted at the PLT entries.
- R_PPC_JMP_SLOT relocs now correctly update the dynamic symbol table. On top of that, the original PLT setup is no longer written here but rather when setting up the PLT section.
- Lazy binding works now that the PLT is actually used. Note that for all of the above to work, the latest binutils are required.
This is all from 2009.
I might be wrong, but it looks like the puts@plt jump is not targeted at a PLT entry.
I might be wrong, but it looks like the puts@plt jump is not targeted at a PLT entry.
I read/objdupo/readelf a lot too, and comes to nearly the same conclusion that the PLT entry seems to be wrong.
The PLT is build by the dynamic linker (elf.library). Reading the ABI the implementation is specific for each OS target. But the lazy variant is to setup traps methods, which gets called resolves the desired address of the function, patches the PLT and/or the calling code (I didn't understand that completely), so that the next call doesn't need to be trapped, but just execute the desired method.
So long for the theory. Disassemble the code and sections etc from the build with the old binutils to the new binutils, i only found differences which I think of might have an impact.
The .rodata section has no standalone read only segment:
Segment/Section old binutils look like this:
Quote:
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x10000000 0x10000000 0x00688 0x01060 R E 0x10000 LOAD 0x002000 0x10002000 0x10002000 0x00034 0x00034 R 0x10000 LOAD 0x002034 0x10012034 0x10012034 0x000f8 0x000f8 RW 0x10000 DYNAMIC 0x002074 0x10012074 0x10012074 0x000a8 0x000a8 RW 0x4
The address and size differs too, but that is just owed to being a newer binutils version. And because .rodata (The read only data section) only contains static data, I don't see how this should affect the PLT stuff. As you too mention, you do not see any reference fo that too.
But modifying the new binutils to let the .rodata section being writable, triggers something in the elf.library that the PLT function call works again. But another side effect is that ctor/dtor call "chaining" is broken.
So i focused to see what else changes if in the new binutils rodata section is set to writeable, the segments/section look like this:
Quote:
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x010000 0x10000000 0x10000000 0x011b8 0x011b8 R E 0x10000 LOAD 0x012000 0x10012000 0x10012000 0x001d8 0x001d8 R 0x10000 LOAD 0x0121d8 0x100221d8 0x100221d8 0x000f0 0x000f0 RW 0x10000 DYNAMIC 0x012200 0x10022200 0x10022200 0x000b8 0x000b8 RW 0x4
Which doesn't make any sense for me to skip that section. But thus the relocation R_PPC_PLTREL24 is missing from the sobj file. So at least this verifies that this relocation is ignored by the elf.library, or better elf.library doesn't care that this is missing.
Anyway without insight what the elf.library actually does for PLT, I think I'm stuck.
Fortunately, I can build elf.library and add extra debug to try to figure out what is going on...
I'm not an expert on dynamic objects, but looking at the .rela.plt section, all the R_PPC_JMP_SLOT symbol values are 0 (readelf -r) with the new binutils. The elf lib uses these values to calculate the "real address", which it then puts in the PLT. I don't think they should be 0 - they need to point to the location of the respective function in .text (at least, that's the case in libc.so).
I don't think they should be 0 - they need to point to the location of the respective function in .text (at least, that's the case in libc.so).
I'm neither an expert, but the value depends on where the function is. If the function is outside the own sobj the value should be zero. (I don't fin the reference anymore) As long i remember the zero indicates to the dynamic linker -> you have to do stuff to find the address of my desired method. A none zero value can directly be used to calculate the address in .text section. But the method must be present in the sobj file. Probably there are more things to consider if going more in detail, but that should be the general path.
An elf.library with more debug out can always help. But maybe you can look at the elf source, and tell how the PLT table is build by the elf.library for sobjs files.
The PLT is build by the dynamic linker (elf.library). Reading the ABI the implementation is specific for each OS target. But the lazy variant is to setup traps methods, which gets called resolves the desired address of the function, patches the PLT and/or the calling code (I didn't understand that completely), so that the next call doesn't need to be trapped, but just execute the desired method.
I'm not 100% sure, but I don't think elf.library works that way. Instead of using traps which resolve it on first access it loads all .so used by the executable and resolves everything before _start() in the executable is called.
I'm afraid I don't have the time to start compiling binutils, so I'm just using your example files that you posted to the beta list.
@MigthyMax
You're right, of course - serves me right for starting to look at this late at night! I've traced through the PLT setup and it does actually look to be working correctly. Instead, the problem appears to be that elf.library is allocating and loading all the sections in segment 1 separately (which includes .text, .plt and .rodota), which I haven't quite figured out yet - it only seems to be seeing the data segment during these searches. The end result is that because R_PPC_PLTREL24 relocs are intentionally ignored, the in place branches jump to the wrong address. For it to work .text and .plt must be located in the same block of memory, so the offsets to the .plt jump table are correct. Just trying to figure out why this isn't happening.
The PLT jump table is created at load time, with the jumps poked into the table when R_PPC_JMP_SLOT relocs are resolved (after other necessary objects have been loaded). The jump table is part of the .plt section, which the fixed branches in .text point to. I don't think the problem is directly related to this now.
Annoyingly I had early ignored "Ignoring this program header because we have an .rodata segment" in the debug output, which you'll see with ELF.debug set to 1, thinking it wasn't relevant. But, this would appear to be the reason for the behaviour I described in my previous message. That is why it works fine when .rodata is in its own segment, because then the segment containing the .text/.plt sections is loaded into a single block of memory, making the PLT branches correct.
Why does elf.library need to do this - to make 68k cross calls work, according to the comments (need to do some more research to understand this!).
So, does the old binutils always put .rodata in its own segment for dynamic objects? I'm sure .text and .rodata have always gone in the same segment for normal non-dynamic stuff.
I'm afraid I don't have the time to start compiling binutils, so I'm just using your example files that you posted to the beta list.
Ok, this test case was without latest change where Max make .rodata be as RW for tests , so .sobjs start to works, but ctor/dtors chaining broken. In the test case which i send to beta-test group, .rodata is RE, and so .sobjs broken, but ctro/dtors chaining works.
So we need .rodata to be R as with adtool's binutils
Quote:
So, does the old binutils always put .rodata in its own segment for dynamic objects? I'm sure .text and .rodata have always gone in the same segment for normal non-dynamic stuff.
At least with this simple test case i send to beta-group builds with adtools version of binutils, we do have that:
test_dyn binary:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x01000034 0x01000034 0x000c0 0x000c0 R E 0x4
INTERP 0x0000f4 0x010000f4 0x010000f4 0x00011 0x00011 R 0x1
[Requesting program interpreter: /usr/lib/ld.so.1]
LOAD 0x000000 0x01000000 0x01000000 0x03a08 0x04078 R E 0x10000
LOAD 0x005000 0x01005000 0x01005000 0x000b0 0x000b0 R 0x10000
LOAD 0x0050b0 0x010150b0 0x010150b0 0x000d8 0x00130 RW 0x10000
DYNAMIC 0x0050c0 0x010150c0 0x010150c0 0x000b8 0x000b8 RW 0x4
Yes, I'm using your test case that had .rodata in the same segment as .text/.plt. This was perfect to exactly understand what was really causing elf.library to crash the code. It had nothing to do with .rodata being in a writable, read-only or executable segment. It was simply the placement - for dynamic objects, .rodata must not be placed in the same segment as .text/.plt with the current elf.library. As you say, .rodata should obviously be read-only. That must be why the old binutils did it that way.
However, I'm pondering whether elf.library needs to do this anymore. So far, I'm struggling to understand what the original reason was, so reluctant to change anything. The change happened in elf.library 53.4:
Quote:
Program header loadable segments are now loaded as segment even if they are executable. This caused some problems with older binaries that have .text and .rodata in the same segment; if the loader detects such a segment, it is ignored and the sections are loaded separately. This behaviour is required because the PLT must be correctly placed relative to the text segment.
Edited by Futaura on 2023/9/8 18:50:12 Edited by Futaura on 2023/9/8 19:50:15
Having modified elf.library to always load the .text/.plt segment in one go, rather than each section separately, the OS won't boot, so looks like I won't be changing that behaviour . Perhaps could be an alignment issue, although not really sure - in both cases, .rodata is marked as read-only, as you would expect.
What matters is that the behaviour was specifically changed to load the .text/.plt segment in one go, mainly to make the PLT stuff work for dynamic objects. So, we know what the correct thing is for binutils to do now.
If there are still issues with ctors/dtors not working, even when .rodata placed in a separate read only segment, I'm happy to check the relevant code in the latest elf.library (I believe Alfkil changed it so that it calls the ctors/dtors directly, right?). Just point me in the direction of a compiled test case, so I can reproduce the issue here.