Skip to content

Commit 877ec97

Browse files
committed
linker/PEi386: Don't sign-extend symbol section number
Previously we incorrectly interpreted PE section numbers as signed values. However, this isn't the case; rather, it's an unsigned 16-bit number with a few special bit-patterns (0xffff and 0xfffe). This resulted in #22941 as the linker would conclude that the sections were invalid. Fixing this required quite a bit of refactoring. Closes #22941.
1 parent a375604 commit 877ec97

File tree

2 files changed

+68
-11
lines changed

2 files changed

+68
-11
lines changed

rts/linker/PEi386.c

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -697,8 +697,16 @@ size_t getSymbolSize ( COFF_HEADER_INFO *info )
697697
}
698698
}
699699

700+
// Constants which may be returned by getSymSectionNumber.
701+
// See https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-number-values
702+
#define PE_SECTION_UNDEFINED ((uint32_t) 0)
703+
#define PE_SECTION_ABSOLUTE ((uint32_t) -1)
704+
#define PE_SECTION_DEBUG ((uint32_t) -2)
705+
706+
// Returns either PE_SECTION_{UNDEFINED,ABSOLUTE,DEBUG} or the (one-based)
707+
// section number of the given symbol.
700708
__attribute__ ((always_inline)) inline
701-
int32_t getSymSectionNumber ( COFF_HEADER_INFO *info, COFF_symbol* sym )
709+
uint32_t getSymSectionNumber ( COFF_HEADER_INFO *info, COFF_symbol* sym )
702710
{
703711
ASSERT(info);
704712
ASSERT(sym);
@@ -707,7 +715,13 @@ int32_t getSymSectionNumber ( COFF_HEADER_INFO *info, COFF_symbol* sym )
707715
case COFF_ANON_BIG_OBJ:
708716
return sym->ex.SectionNumber;
709717
default:
710-
return sym->og.SectionNumber;
718+
// Take care to only sign-extend reserved values; see #22941.
719+
switch (sym->og.SectionNumber) {
720+
case IMAGE_SYM_UNDEFINED: return PE_SECTION_UNDEFINED;
721+
case IMAGE_SYM_ABSOLUTE : return PE_SECTION_ABSOLUTE;
722+
case IMAGE_SYM_DEBUG: return PE_SECTION_DEBUG;
723+
default: return (uint16_t) sym->og.SectionNumber;
724+
}
711725
}
712726
}
713727

@@ -1652,7 +1666,7 @@ ocGetNames_PEi386 ( ObjectCode* oc )
16521666
StgWord globalBssSize = 0;
16531667
for (unsigned int i=0; i < info->numberOfSymbols; i++) {
16541668
COFF_symbol* sym = &oc->info->symbols[i];
1655-
if (getSymSectionNumber (info, sym) == IMAGE_SYM_UNDEFINED
1669+
if (getSymSectionNumber (info, sym) == PE_SECTION_UNDEFINED
16561670
&& getSymValue (info, sym) > 0
16571671
&& getSymStorageClass (info, sym) != IMAGE_SYM_CLASS_SECTION) {
16581672
globalBssSize += getSymValue (info, sym);
@@ -1685,21 +1699,39 @@ ocGetNames_PEi386 ( ObjectCode* oc )
16851699
for (unsigned int i = 0; i < (uint32_t)oc->n_symbols; i++) {
16861700
COFF_symbol* sym = &oc->info->symbols[i];
16871701

1688-
int32_t secNumber = getSymSectionNumber (info, sym);
16891702
uint32_t symValue = getSymValue (info, sym);
16901703
uint8_t symStorageClass = getSymStorageClass (info, sym);
1691-
16921704
SymbolAddr *addr = NULL;
16931705
bool isWeak = false;
16941706
SymbolName *sname = get_sym_name (getSymShortName (info, sym), oc);
1695-
Section *section = secNumber > 0 ? &oc->sections[secNumber-1] : NULL;
1707+
1708+
uint32_t secNumber = getSymSectionNumber (info, sym);
1709+
Section *section;
1710+
switch (secNumber) {
1711+
case PE_SECTION_UNDEFINED:
1712+
// N.B. This may be a weak symbol
1713+
section = NULL;
1714+
break;
1715+
case PE_SECTION_ABSOLUTE:
1716+
IF_DEBUG(linker, debugBelch("symbol %s is ABSOLUTE, skipping...\n", sname));
1717+
i += getSymNumberOfAuxSymbols (info, sym);
1718+
continue;
1719+
case PE_SECTION_DEBUG:
1720+
IF_DEBUG(linker, debugBelch("symbol %s is DEBUG, skipping...\n", sname));
1721+
i += getSymNumberOfAuxSymbols (info, sym);
1722+
continue;
1723+
default:
1724+
CHECK(secNumber < (uint32_t) oc->n_sections);
1725+
section = &oc->sections[secNumber-1];
1726+
}
16961727

16971728
SymType type;
16981729
switch (getSymType(oc->info->ch_info, sym)) {
16991730
case 0x00: type = SYM_TYPE_DATA; break;
17001731
case 0x20: type = SYM_TYPE_CODE; break;
17011732
default:
1702-
debugBelch("Invalid symbol type: 0x%x\n", getSymType(oc->info->ch_info, sym));
1733+
debugBelch("Symbol %s has invalid type 0x%x\n",
1734+
sname, getSymType(oc->info->ch_info, sym));
17031735
return 1;
17041736
}
17051737

@@ -1730,8 +1762,18 @@ ocGetNames_PEi386 ( ObjectCode* oc )
17301762
CHECK(symValue == 0);
17311763
COFF_symbol_aux_weak_external *aux = (COFF_symbol_aux_weak_external *) (sym+1);
17321764
COFF_symbol* targetSym = &oc->info->symbols[aux->TagIndex];
1733-
int32_t targetSecNumber = getSymSectionNumber (info, targetSym);
1734-
Section *targetSection = targetSecNumber > 0 ? &oc->sections[targetSecNumber-1] : NULL;
1765+
1766+
uint32_t targetSecNumber = getSymSectionNumber (info, targetSym);
1767+
Section *targetSection;
1768+
switch (targetSecNumber) {
1769+
case PE_SECTION_UNDEFINED:
1770+
case PE_SECTION_ABSOLUTE:
1771+
case PE_SECTION_DEBUG:
1772+
targetSection = NULL;
1773+
break;
1774+
default:
1775+
targetSection = &oc->sections[targetSecNumber-1];
1776+
}
17351777
addr = (SymbolAddr*) ((size_t) targetSection->start + getSymValue(info, targetSym));
17361778
}
17371779
else if ( secNumber == IMAGE_SYM_UNDEFINED && symValue > 0) {
@@ -1850,6 +1892,9 @@ ocGetNames_PEi386 ( ObjectCode* oc )
18501892
return false;
18511893

18521894
break;
1895+
} else if (secNumber == PE_SECTION_UNDEFINED) {
1896+
IF_DEBUG(linker, debugBelch("symbol %s is UNDEFINED, skipping...\n", sname));
1897+
i += getSymNumberOfAuxSymbols (info, sym);
18531898
}
18541899

18551900
if ((addr != NULL || isWeak)
@@ -1976,7 +2021,19 @@ ocResolve_PEi386 ( ObjectCode* oc )
19762021
debugBelch("'\n" ));
19772022

19782023
if (getSymStorageClass (info, sym) == IMAGE_SYM_CLASS_STATIC) {
1979-
Section section = oc->sections[getSymSectionNumber (info, sym)-1];
2024+
uint32_t sect_n = getSymSectionNumber (info, sym);
2025+
switch (sect_n) {
2026+
case PE_SECTION_UNDEFINED:
2027+
case PE_SECTION_ABSOLUTE:
2028+
case PE_SECTION_DEBUG:
2029+
errorBelch(" | %" PATH_FMT ": symbol `%s' has invalid section number %02x",
2030+
oc->fileName, symbol, sect_n);
2031+
return false;
2032+
default:
2033+
break;
2034+
}
2035+
CHECK(sect_n < (uint32_t) oc->n_sections);
2036+
Section section = oc->sections[sect_n - 1];
19802037
S = ((size_t)(section.start))
19812038
+ ((size_t)(getSymValue (info, sym)));
19822039
} else {

rts/linker/PEi386.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ struct _Alignments {
143143
COFF_OBJ_TYPE getObjectType ( char* image, pathchar* fileName );
144144
COFF_HEADER_INFO* getHeaderInfo ( ObjectCode* oc );
145145
size_t getSymbolSize ( COFF_HEADER_INFO *info );
146-
int32_t getSymSectionNumber ( COFF_HEADER_INFO *info, COFF_symbol* sym );
146+
uint32_t getSymSectionNumber ( COFF_HEADER_INFO *info, COFF_symbol* sym );
147147
uint32_t getSymValue ( COFF_HEADER_INFO *info, COFF_symbol* sym );
148148
uint8_t getSymStorageClass ( COFF_HEADER_INFO *info, COFF_symbol* sym );
149149
uint8_t getSymNumberOfAuxSymbols ( COFF_HEADER_INFO *info, COFF_symbol* sym );

0 commit comments

Comments
 (0)