diff options
author | Tamar Christina <tamar@zhox.com> | 2016-06-11 10:18:19 +0200 |
---|---|---|
committer | Tamar Christina <tamar@zhox.com> | 2016-06-12 13:43:32 +0200 |
commit | b40e1b4c6746bdc34e6a53548a3925d309201c4d (patch) | |
tree | 8db58488f701c2dc944714406ebf5fc9a4bbcf6f /rts/Linker.c | |
parent | 913086797af8060808973e8f6a11a3702afffe14 (diff) | |
download | haskell-b40e1b4c6746bdc34e6a53548a3925d309201c4d.tar.gz |
Fix incorrect calculated relocations on Windows x86_64
Summary:
See #12031 for analysis, but essentially what happens is:
To sum up the issue, the reason this seems to go wrong is because
of how we initialize the `.bss` section for Windows in the runtime linker.
The first issue is where we calculate the zero space for the section:
```
zspace = stgCallocBytes(1, bss_sz, "ocGetNames_PEi386(anonymous bss)");
sectab_i->PointerToRawData = ((UChar*)zspace) - ((UChar*)(oc->image));
```
Where
```
UInt32 PointerToRawData;
```
This means we're stuffing a `64-bit` value into a `32-bit` one. Also `zspace`
can be larger than `oc->image`. In which case it'll overflow and
then get truncated in the cast.
The address of a value in the `.bss` section is then calculated as:
```
addr = ((UChar*)(oc->image))
+ (sectabent->PointerToRawData
+ symtab_i->Value);
```
If it does truncate then this calculation won't be correct (which is what is happening).
We then later use the value of `addr` as the `S` (Symbol) value for the relocations
```
S = (size_t) lookupSymbol_( (char*)symbol );
```
Now the majority of the relocations are `R_X86_64_PC32` etc.
e.g. They are guaranteed to fit in a `32-bit` value.
The `R_X86_64_64` introduced for these pseudo-relocations so they can use
the full `48-bit` addressing space isn't as lucky.
As for why it sometimes work has to do on whether the value is truncated or not.
`PointerToRawData` can't be changed because it's size is fixed by the PE specification.
Instead just like with the other platforms, we now use `section` on Windows as well.
This gives us a `start` parameter of type `void*` which solves the issue.
This refactors the code to use `section.start` and to fix the issues.
Test Plan: ./validate and new test added T12031
Reviewers: RyanGlScott, erikd, bgamari, austin, simonmar
Reviewed By: simonmar
Subscribers: thomie, #ghc_windows_task_force
Differential Revision: https://phabricator.haskell.org/D2316
GHC Trac Issues: #12031, #11317
Diffstat (limited to 'rts/Linker.c')
-rw-r--r-- | rts/Linker.c | 73 |
1 files changed, 42 insertions, 31 deletions
diff --git a/rts/Linker.c b/rts/Linker.c index a1f72e5cca..ef909f0ad9 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -3497,6 +3497,26 @@ ocVerifyImage_PEi386 ( ObjectCode* oc ) } #endif + /* .BSS Section is initialized in ocGetNames_PEi386 + but we need the Sections array initialized here already. */ + Section *sections; + sections = (Section*)stgCallocBytes( + sizeof(Section), + hdr->NumberOfSections + 1, /* +1 for the global BSS section see ocGetNames_PEi386 */ + "ocVerifyImage_PEi386(sections)"); + oc->sections = sections; + oc->n_sections = hdr->NumberOfSections + 1; + + /* Initialize the Sections */ + for (i = 0; i < hdr->NumberOfSections; i++) { + COFF_section* sectab_i + = (COFF_section*) + myindex(sizeof_COFF_section, sectab, i); + + /* Calculate the start of the data section */ + sections[i].start = oc->image + sectab_i->PointerToRawData; + } + /* No further verification after this point; only debug printing. */ i = 0; IF_DEBUG(linker, i=1); @@ -3525,6 +3545,7 @@ ocVerifyImage_PEi386 ( ObjectCode* oc ) COFF_section* sectab_i = (COFF_section*) myindex ( sizeof_COFF_section, sectab, i ); + Section section = sections[i]; debugBelch( "\n" "section %d\n" @@ -3537,14 +3558,14 @@ ocVerifyImage_PEi386 ( ObjectCode* oc ) " vsize %d\n" " vaddr %d\n" " data sz %d\n" - " data off %d\n" + " data off 0x%p\n" " num rel %d\n" " off rel %d\n" " ptr raw 0x%x\n", sectab_i->VirtualSize, sectab_i->VirtualAddress, sectab_i->SizeOfRawData, - sectab_i->PointerToRawData, + section.start, sectab_i->NumberOfRelocations, sectab_i->PointerToRelocations, sectab_i->PointerToRawData @@ -3690,25 +3711,16 @@ ocGetNames_PEi386 ( ObjectCode* oc ) * triggered this is libraries/base/cbits/dirUtils.c:__hscore_getFolderPath()) */ if (sectab_i->VirtualSize == 0 && sectab_i->SizeOfRawData == 0) continue; - /* This is a non-empty .bss section. Allocate zeroed space for - it, and set its PointerToRawData field such that oc->image + - PointerToRawData == addr_of_zeroed_space. */ + /* This is a non-empty .bss section. + Allocate zeroed space for it */ bss_sz = sectab_i->VirtualSize; if ( bss_sz < sectab_i->SizeOfRawData) { bss_sz = sectab_i->SizeOfRawData; } zspace = stgCallocBytes(1, bss_sz, "ocGetNames_PEi386(anonymous bss)"); - sectab_i->PointerToRawData = ((UChar*)zspace) - ((UChar*)(oc->image)); + oc->sections[i].start = zspace; addProddableBlock(oc, zspace, bss_sz); /* debugBelch("BSS anon section at 0x%x\n", zspace); */ } - Section *sections; - sections = (Section*)stgCallocBytes( - sizeof(Section), - hdr->NumberOfSections + 1, /* +1 for the global BSS section see below */ - "ocGetNames_PEi386(sections)"); - oc->sections = sections; - oc->n_sections = hdr->NumberOfSections + 1; - /* Copy section information into the ObjectCode. */ for (i = 0; i < hdr->NumberOfSections; i++) { @@ -3722,6 +3734,7 @@ ocGetNames_PEi386 ( ObjectCode* oc ) COFF_section* sectab_i = (COFF_section*) myindex ( sizeof_COFF_section, sectab, i ); + Section section = oc->sections[i]; char *secname = cstring_from_section_name(sectab_i->Name, strtab); @@ -3748,11 +3761,11 @@ ocGetNames_PEi386 ( ObjectCode* oc ) sz = sectab_i->SizeOfRawData; if (sz < sectab_i->VirtualSize) sz = sectab_i->VirtualSize; - start = ((UChar*)(oc->image)) + sectab_i->PointerToRawData; + start = section.start; end = start + sz - 1; if (kind != SECTIONKIND_OTHER && end >= start) { - addSection(§ions[i], kind, SECTION_NOMEM, start, sz, 0, 0, 0); + addSection(&oc->sections[i], kind, SECTION_NOMEM, start, sz, 0, 0, 0); addProddableBlock(oc, start, sz); } @@ -3784,13 +3797,13 @@ ocGetNames_PEi386 ( ObjectCode* oc ) if (globalBssSize > 0) { bss = stgCallocBytes(1, globalBssSize, "ocGetNames_PEi386(non-anonymous bss)"); - addSection(§ions[oc->n_sections-1], + addSection(&oc->sections[oc->n_sections-1], SECTIONKIND_RWDATA, SECTION_MALLOC, bss, globalBssSize, 0, 0, 0); IF_DEBUG(linker, debugBelch("bss @ %p %" FMT_Word "\n", bss, globalBssSize)); addProddableBlock(oc, bss, globalBssSize); } else { - addSection(§ions[oc->n_sections-1], + addSection(&oc->sections[oc->n_sections-1], SECTIONKIND_OTHER, SECTION_NOMEM, NULL, 0, 0, 0, 0); } @@ -3817,9 +3830,8 @@ ocGetNames_PEi386 ( ObjectCode* oc ) || ( symtab_i->StorageClass == MYIMAGE_SYM_CLASS_STATIC && sectabent->Characteristics & MYIMAGE_SCN_LNK_COMDAT) ) { - addr = ((UChar*)(oc->image)) - + (sectabent->PointerToRawData - + symtab_i->Value); + addr = (void*)((size_t)oc->sections[symtab_i->SectionNumber-1].start + + symtab_i->Value); if (sectabent->Characteristics & MYIMAGE_SCN_LNK_COMDAT) { isWeak = HS_BOOL_TRUE; } @@ -3969,6 +3981,7 @@ ocResolve_PEi386 ( ObjectCode* oc ) = (COFF_reloc*) ( ((UChar*)(oc->image)) + sectab_i->PointerToRelocations ); + Section section = oc->sections[i]; char *secname = cstring_from_section_name(sectab_i->Name, strtab); @@ -4020,11 +4033,10 @@ ocResolve_PEi386 ( ObjectCode* oc ) myindex ( sizeof_COFF_reloc, reltab, j ); /* the location to patch */ - pP = ( - ((UChar*)(oc->image)) - + (sectab_i->PointerToRawData - + reltab_j->VirtualAddress - - sectab_i->VirtualAddress ) + pP = (void*)( + (size_t)section.start + + reltab_j->VirtualAddress + - sectab_i->VirtualAddress ); /* the existing contents of pP */ A = *(UInt32*)pP; @@ -4043,10 +4055,8 @@ ocResolve_PEi386 ( ObjectCode* oc ) debugBelch("'\n" )); if (sym->StorageClass == MYIMAGE_SYM_CLASS_STATIC) { - COFF_section* section_sym - = (COFF_section*) myindex ( sizeof_COFF_section, sectab, sym->SectionNumber-1 ); - S = ((size_t)(oc->image)) - + ((size_t)(section_sym->PointerToRawData)) + Section section = oc->sections[sym->SectionNumber-1]; + S = ((size_t)(section.start)) + ((size_t)(sym->Value)); } else { copyName ( sym->Name, strtab, symbol, 1000-1 ); @@ -4201,9 +4211,10 @@ ocRunInit_PEi386 ( ObjectCode *oc ) COFF_section* sectab_i = (COFF_section*) myindex ( sizeof_COFF_section, sectab, i ); + Section section = oc->sections[i]; char *secname = cstring_from_section_name(sectab_i->Name, strtab); if (0 == strcmp(".ctors", (char*)secname)) { - UChar *init_startC = (UChar*)(oc->image) + sectab_i->PointerToRawData; + UChar *init_startC = section.start; init_t *init_start, *init_end, *init; init_start = (init_t*)init_startC; init_end = (init_t*)(init_startC + sectab_i->SizeOfRawData); |