mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Ensure that alignment is at most 16 and we do not cause UB
This commit is contained in:
@@ -105,7 +105,6 @@ Section *sect_FindSectionByName(std::string const &name) {
|
|||||||
return search != sectionMap.end() ? §ionList[search->second] : nullptr;
|
return search != sectionMap.end() ? §ionList[search->second] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define mask(align) ((1U << (align)) - 1)
|
|
||||||
#define sectError(...) \
|
#define sectError(...) \
|
||||||
do { \
|
do { \
|
||||||
error(__VA_ARGS__); \
|
error(__VA_ARGS__); \
|
||||||
@@ -115,9 +114,16 @@ Section *sect_FindSectionByName(std::string const &name) {
|
|||||||
static unsigned int mergeSectUnion(
|
static unsigned int mergeSectUnion(
|
||||||
Section §, SectionType type, uint32_t org, uint8_t alignment, uint16_t alignOffset
|
Section §, SectionType type, uint32_t org, uint8_t alignment, uint16_t alignOffset
|
||||||
) {
|
) {
|
||||||
assume(alignment < 16); // Should be ensured by the caller
|
|
||||||
unsigned int nbSectErrors = 0;
|
unsigned int nbSectErrors = 0;
|
||||||
|
|
||||||
|
assume(alignment < 16); // Should be ensured by the caller
|
||||||
|
uint32_t alignSize = 1u << alignment;
|
||||||
|
uint32_t alignMask = alignSize - 1;
|
||||||
|
|
||||||
|
assume(sect.align <= 16); // Left-shifting by 32 or more would be UB
|
||||||
|
uint32_t sectAlignSize = 1u << sect.align;
|
||||||
|
uint32_t sectAlignMask = sectAlignSize - 1;
|
||||||
|
|
||||||
// Unionized sections only need "compatible" constraints, and they end up with the strictest
|
// Unionized sections only need "compatible" constraints, and they end up with the strictest
|
||||||
// combination of both.
|
// combination of both.
|
||||||
if (sect_HasData(type)) {
|
if (sect_HasData(type)) {
|
||||||
@@ -130,10 +136,10 @@ static unsigned int mergeSectUnion(
|
|||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at different address $%04" PRIx32, sect.org
|
"Section already declared as fixed at different address $%04" PRIx32, sect.org
|
||||||
);
|
);
|
||||||
} else if (sect.align != 0 && (mask(sect.align) & (org - sect.alignOfs))) {
|
} else if (sect.align != 0 && ((org - sect.alignOfs) & sectAlignMask)) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")",
|
"Section already declared as aligned to %" PRIu32 " bytes (offset %" PRIu16 ")",
|
||||||
1U << sect.align,
|
sectAlignSize,
|
||||||
sect.alignOfs
|
sect.alignOfs
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -144,17 +150,18 @@ static unsigned int mergeSectUnion(
|
|||||||
} else if (alignment != 0) {
|
} else if (alignment != 0) {
|
||||||
// Make sure any fixed address given is compatible
|
// Make sure any fixed address given is compatible
|
||||||
if (sect.org != UINT32_MAX) {
|
if (sect.org != UINT32_MAX) {
|
||||||
if ((sect.org - alignOffset) & mask(alignment)) {
|
if ((sect.org - alignOffset) & alignMask) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at incompatible address $%04" PRIx32,
|
"Section already declared as fixed at incompatible address $%04" PRIx32,
|
||||||
sect.org
|
sect.org
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Check if alignment offsets are compatible
|
// Check if alignment offsets are compatible
|
||||||
} else if ((alignOffset & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
} else if ((alignOffset & sectAlignMask) != (sect.alignOfs & alignMask)) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared with incompatible %u-byte alignment (offset %" PRIu16 ")",
|
"Section already declared with incompatible %" PRIu32
|
||||||
1U << sect.align,
|
"-byte alignment (offset %" PRIu16 ")",
|
||||||
|
sectAlignSize,
|
||||||
sect.alignOfs
|
sect.alignOfs
|
||||||
);
|
);
|
||||||
} else if (alignment > sect.align) {
|
} else if (alignment > sect.align) {
|
||||||
@@ -169,9 +176,16 @@ static unsigned int mergeSectUnion(
|
|||||||
|
|
||||||
static unsigned int
|
static unsigned int
|
||||||
mergeFragments(Section §, uint32_t org, uint8_t alignment, uint16_t alignOffset) {
|
mergeFragments(Section §, uint32_t org, uint8_t alignment, uint16_t alignOffset) {
|
||||||
assume(alignment < 16); // Should be ensured by the caller
|
|
||||||
unsigned int nbSectErrors = 0;
|
unsigned int nbSectErrors = 0;
|
||||||
|
|
||||||
|
assume(alignment < 16); // Should be ensured by the caller
|
||||||
|
uint32_t alignSize = 1u << alignment;
|
||||||
|
uint32_t alignMask = alignSize - 1;
|
||||||
|
|
||||||
|
assume(sect.align <= 16); // Left-shifting by 32 or more would be UB
|
||||||
|
uint32_t sectAlignSize = 1u << sect.align;
|
||||||
|
uint32_t sectAlignMask = sectAlignSize - 1;
|
||||||
|
|
||||||
// Fragments only need "compatible" constraints, and they end up with the strictest
|
// Fragments only need "compatible" constraints, and they end up with the strictest
|
||||||
// combination of both.
|
// combination of both.
|
||||||
// The merging is however performed at the *end* of the original section!
|
// The merging is however performed at the *end* of the original section!
|
||||||
@@ -183,10 +197,10 @@ static unsigned int
|
|||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at incompatible address $%04" PRIx32, sect.org
|
"Section already declared as fixed at incompatible address $%04" PRIx32, sect.org
|
||||||
);
|
);
|
||||||
} else if (sect.align != 0 && (mask(sect.align) & (curOrg - sect.alignOfs))) {
|
} else if (sect.align != 0 && ((curOrg - sect.alignOfs) & sectAlignMask)) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")",
|
"Section already declared as aligned to %" PRIu32 " bytes (offset %" PRIu16 ")",
|
||||||
1U << sect.align,
|
sectAlignSize,
|
||||||
sect.alignOfs
|
sect.alignOfs
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -195,25 +209,26 @@ static unsigned int
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (alignment != 0) {
|
} else if (alignment != 0) {
|
||||||
int32_t curOfs = (alignOffset - sect.size) % (1U << alignment);
|
int32_t curOfs = (alignOffset - sect.size) % alignSize;
|
||||||
|
|
||||||
if (curOfs < 0) {
|
if (curOfs < 0) {
|
||||||
curOfs += 1U << alignment;
|
curOfs += alignSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure any fixed address given is compatible
|
// Make sure any fixed address given is compatible
|
||||||
if (sect.org != UINT32_MAX) {
|
if (sect.org != UINT32_MAX) {
|
||||||
if ((sect.org - curOfs) & mask(alignment)) {
|
if ((sect.org - curOfs) & alignMask) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at incompatible address $%04" PRIx32,
|
"Section already declared as fixed at incompatible address $%04" PRIx32,
|
||||||
sect.org
|
sect.org
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Check if alignment offsets are compatible
|
// Check if alignment offsets are compatible
|
||||||
} else if ((curOfs & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
} else if ((curOfs & sectAlignMask) != (sect.alignOfs & alignMask)) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared with incompatible %u-byte alignment (offset %" PRIu16 ")",
|
"Section already declared with incompatible %" PRIu32
|
||||||
1U << sect.align,
|
"-byte alignment (offset %" PRIu16 ")",
|
||||||
|
sectAlignSize,
|
||||||
sect.alignOfs
|
sect.alignOfs
|
||||||
);
|
);
|
||||||
} else if (alignment > sect.align) {
|
} else if (alignment > sect.align) {
|
||||||
@@ -356,6 +371,10 @@ static Section *getSection(
|
|||||||
uint8_t alignment = attrs.alignment;
|
uint8_t alignment = attrs.alignment;
|
||||||
uint16_t alignOffset = attrs.alignOfs;
|
uint16_t alignOffset = attrs.alignOfs;
|
||||||
|
|
||||||
|
assume(alignment <= 16); // Should be ensured by the caller
|
||||||
|
uint32_t alignSize = 1u << alignment;
|
||||||
|
uint32_t alignMask = alignSize - 1;
|
||||||
|
|
||||||
// First, validate parameters, and normalize them if applicable
|
// First, validate parameters, and normalize them if applicable
|
||||||
|
|
||||||
if (bank != UINT32_MAX) {
|
if (bank != UINT32_MAX) {
|
||||||
@@ -377,11 +396,11 @@ static Section *getSection(
|
|||||||
bank = sectionTypeInfo[type].firstBank;
|
bank = sectionTypeInfo[type].firstBank;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alignOffset >= 1 << alignment) {
|
if (alignOffset >= alignSize) {
|
||||||
error(
|
error(
|
||||||
"Alignment offset (%" PRIu16 ") must be smaller than alignment size (%u)",
|
"Alignment offset (%" PRIu16 ") must be smaller than alignment size (%" PRIu32 ")",
|
||||||
alignOffset,
|
alignOffset,
|
||||||
1U << alignment
|
alignSize
|
||||||
);
|
);
|
||||||
alignOffset = 0;
|
alignOffset = 0;
|
||||||
}
|
}
|
||||||
@@ -400,19 +419,13 @@ static Section *getSection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (alignment != 0) {
|
if (alignment != 0) {
|
||||||
if (alignment > 16) {
|
|
||||||
error("Alignment must be between 0 and 16, not %u", alignment);
|
|
||||||
alignment = 16;
|
|
||||||
}
|
|
||||||
// It doesn't make sense to have both alignment and org set
|
// It doesn't make sense to have both alignment and org set
|
||||||
uint32_t mask = mask(alignment);
|
|
||||||
|
|
||||||
if (org != UINT32_MAX) {
|
if (org != UINT32_MAX) {
|
||||||
if ((org - alignOffset) & mask) {
|
if ((org - alignOffset) & alignMask) {
|
||||||
error("Section \"%s\"'s fixed address doesn't match its alignment", name.c_str());
|
error("Section \"%s\"'s fixed address doesn't match its alignment", name.c_str());
|
||||||
}
|
}
|
||||||
alignment = 0; // Ignore it if it's satisfied
|
alignment = 0; // Ignore it if it's satisfied
|
||||||
} else if (sectionTypeInfo[type].startAddr & mask) {
|
} else if (sectionTypeInfo[type].startAddr & alignMask) {
|
||||||
error(
|
error(
|
||||||
"Section \"%s\"'s alignment cannot be attained in %s",
|
"Section \"%s\"'s alignment cannot be attained in %s",
|
||||||
name.c_str(),
|
name.c_str(),
|
||||||
@@ -609,8 +622,12 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assume(alignment <= 16); // Should be ensured by the caller
|
||||||
|
uint32_t alignSize = 1u << alignment;
|
||||||
|
|
||||||
Section *sect = sect_GetSymbolSection();
|
Section *sect = sect_GetSymbolSection();
|
||||||
uint32_t alignSize = 1 << alignment; // Size of an aligned "block"
|
assume(sect->align <= 16); // Left-shifting by 32 or more would be UB
|
||||||
|
uint32_t sectAlignSize = 1u << sect->align;
|
||||||
|
|
||||||
if (sect->org != UINT32_MAX) {
|
if (sect->org != UINT32_MAX) {
|
||||||
if (uint32_t actualOffset = (sect->org + curOffset) % alignSize; actualOffset != offset) {
|
if (uint32_t actualOffset = (sect->org + curOffset) % alignSize; actualOffset != offset) {
|
||||||
@@ -624,26 +641,20 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
|
|||||||
actualOffset
|
actualOffset
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (uint32_t actualOffset = (sect->alignOfs + curOffset) % alignSize;
|
||||||
if (uint32_t actualOffset = (sect->alignOfs + curOffset) % alignSize,
|
|
||||||
sectAlignSize = 1 << sect->align;
|
|
||||||
sect->align != 0 && actualOffset % sectAlignSize != offset % sectAlignSize) {
|
sect->align != 0 && actualOffset % sectAlignSize != offset % sectAlignSize) {
|
||||||
error(
|
error(
|
||||||
"Section is misaligned ($%04" PRIx32
|
"Section is misaligned ($%04" PRIx32 " bytes into the section, expected ALIGN[%" PRIu32
|
||||||
" bytes into the section, expected ALIGN[%" PRIu32 ", %" PRIu32
|
", %" PRIu32 "], got ALIGN[%" PRIu32 ", %" PRIu32 "])",
|
||||||
"], got ALIGN[%" PRIu32 ", %" PRIu32 "])",
|
|
||||||
curOffset,
|
curOffset,
|
||||||
alignment,
|
alignment,
|
||||||
offset,
|
offset,
|
||||||
alignment,
|
alignment,
|
||||||
actualOffset
|
actualOffset
|
||||||
);
|
);
|
||||||
} else if (alignment >= 16) {
|
} else if (alignment == 16) {
|
||||||
// Treat an alignment large enough as fixing the address.
|
// Treat an alignment large enough as fixing the address.
|
||||||
// Note that this also ensures that a section's alignment never becomes 16 or greater.
|
// Note that this also ensures that a section's alignment never becomes 16 or greater.
|
||||||
if (alignment > 16) {
|
|
||||||
error("Alignment must be between 0 and 16, not %u", alignment);
|
|
||||||
}
|
|
||||||
sect->align = 0; // Reset the alignment, since we're fixing the address.
|
sect->align = 0; // Reset the alignment, since we're fixing the address.
|
||||||
sect->org = offset - curOffset;
|
sect->org = offset - curOffset;
|
||||||
} else if (alignment > sect->align) {
|
} else if (alignment > sect->align) {
|
||||||
@@ -651,7 +662,6 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
|
|||||||
// We need `(sect->alignOfs + curOffset) % alignSize == offset`
|
// We need `(sect->alignOfs + curOffset) % alignSize == offset`
|
||||||
sect->alignOfs = (offset - curOffset) % alignSize;
|
sect->alignOfs = (offset - curOffset) % alignSize;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void growSection(uint32_t growth) {
|
static void growSection(uint32_t growth) {
|
||||||
|
|||||||
Reference in New Issue
Block a user