- /* Walk through each of the memory sections. Add up the sizes and check
- * for an overflow of the section. Assign the start addresses of the
- * segments while doing this.
- */
- Memory* M = MemoryList;
- while (M) {
-
- /* Get the start address of this memory area */
- unsigned long Addr = M->Start;
-
- /* Walk through the segments in this memory area */
- MemListNode* N = M->SegList;
- while (N) {
-
- /* Get the segment from the node */
- SegDesc* S = N->Seg;
-
- /* Handle ALIGN and OFFSET/START */
- if (S->Flags & SF_ALIGN) {
- /* Align the address */
- unsigned long Val = (0x01UL << S->Align) - 1;
- Addr = (Addr + Val) & ~Val;
- } else if (S->Flags & (SF_OFFSET | SF_START)) {
- /* Give the segment a fixed starting address */
- unsigned long NewAddr = S->Addr;
- if (S->Flags & SF_OFFSET) {
- /* An offset was given, no address, make an address */
- NewAddr += M->Start;
- }
- if (Addr > NewAddr) {
- /* Offset already too large */
- if (S->Flags & SF_OFFSET) {
- Error ("Offset too small in `%s', segment `%s'",
- M->Name, S->Name);
- } else {
- Error ("Start address too low in `%s', segment `%s'",
- M->Name, S->Name);
- }
- }
- Addr = NewAddr;
- }
-
- /* If this is the run area, set the start address of this segment */
- if (S->Run == M) {
- S->Seg->PC = Addr;
- }
-
- /* Increment the fill level of the memory area and check for an
- * overflow.
- */
- M->FillLevel = Addr + S->Seg->Size - M->Start;
- if (M->FillLevel > M->Size) {
- Error ("Memory area overflow in `%s', segment `%s' (%lu bytes)",
- M->Name, S->Name, M->FillLevel - M->Size);
- }
-
- /* If requested, define symbols for the start and size of the
- * segment.
- */
- if (S->Flags & SF_DEFINE) {
- if ((S->Flags & SF_LOAD_AND_RUN) && S->Run == S->Load) {
- /* RUN and LOAD given and in one memory area.
- * Be careful: We will encounter this code twice, the
- * first time when walking the RUN list, second time when
- * walking the LOAD list. Be sure to define only the
- * relevant symbols on each walk.
- */
- if (S->Load == M) {
- if ((S->Flags & SF_LOAD_DEF) == 0) {
- CreateLoadDefines (M, S);
- } else {
- CHECK ((S->Flags & SF_RUN_DEF) == 0);
- CreateRunDefines (S);
- }
- }
- } else {
- /* RUN and LOAD in different memory areas, or RUN not
- * given, so RUN defaults to LOAD. In the latter case, we
- * have only one copy of the segment in the area.
- */
- if (S->Run == M) {
- CreateRunDefines (S);
- }
- if (S->Load == M) {
- CreateLoadDefines (M, S);
- }
- }
- }
-
- /* Calculate the new address */
- Addr += S->Seg->Size;
-
- /* Next segment */
- N = N->Next;
- }
-
- /* If requested, define symbols for start and size of the memory area */
- if (M->Flags & MF_DEFINE) {
- char Buf [256];
- sprintf (Buf, "__%s_START__", M->Name);
- CreateMemoryExport (Buf, M, 0);
- sprintf (Buf, "__%s_SIZE__", M->Name);
- CreateConstExport (Buf, M->Size);
- sprintf (Buf, "__%s_LAST__", M->Name);
- CreateConstExport (Buf, M->FillLevel);
- }
-
- /* Next memory area */
- M = M->Next;
+ unsigned Overflows = 0;
+ unsigned I;
+
+ /* Postprocess symbols. We must do that first, since weak symbols are
+ ** defined here, which may be needed later.
+ */
+ ProcessSymbols ();
+
+ /* Postprocess segments */
+ ProcessSegments ();
+
+ /* Walk through each of the memory sections. Add up the sizes; and, check
+ ** for an overflow of the section. Assign the start addresses of the
+ ** segments while doing that.
+ */
+ for (I = 0; I < CollCount (&MemoryAreas); ++I) {
+ unsigned J;
+ unsigned long Addr;
+ unsigned Overwrites = 0;
+
+ /* Get the next memory area */
+ MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
+
+ /* Remember the offset in the output file */
+ M->FileOffs = M->F->Size;
+
+ /* Remember if this is a relocatable memory area */
+ M->Relocatable = RelocatableBinFmt (M->F->Format);
+
+ /* Resolve the start address expression, remember the start address,
+ ** and mark the memory area as placed.
+ */
+ if (!IsConstExpr (M->StartExpr)) {
+ CfgError (GetSourcePos (M->LI),
+ "Start address of memory area '%s' is not constant",
+ GetString (M->Name));
+ }
+ Addr = M->Start = GetExprVal (M->StartExpr);
+ M->Flags |= MF_PLACED;
+
+ /* If requested, define the symbol for the start of the memory area.
+ ** Doing it here means that the expression for the size of the area
+ ** may reference this symbol.
+ */
+ if (M->Flags & MF_DEFINE) {
+ Export* E;
+ StrBuf Buf = STATIC_STRBUF_INITIALIZER;
+
+ /* Define the start of the memory area */
+ SB_Printf (&Buf, "__%s_START__", GetString (M->Name));
+ E = CreateMemoryExport (GetStrBufId (&Buf), M, 0);
+ CollAppend (&E->DefLines, M->LI);
+
+ SB_Done (&Buf);
+ }
+
+ /* Resolve the size expression */
+ if (!IsConstExpr (M->SizeExpr)) {
+ CfgError (GetSourcePos (M->LI),
+ "Size of memory area '%s' is not constant",
+ GetString (M->Name));
+ }
+ M->Size = GetExprVal (M->SizeExpr);
+
+ /* Walk through the segments in this memory area */
+ for (J = 0; J < CollCount (&M->SegList); ++J) {
+ /* Get the segment */
+ SegDesc* S = CollAtUnchecked (&M->SegList, J);
+
+ /* Remember the start address before handling this segment */
+ unsigned long StartAddr = Addr;
+
+ /* Take note of "overwrite" segments and make sure there are no
+ ** other segment types following them in current memory region.
+ */
+ if (S->Flags & SF_OVERWRITE) {
+ if (S->Flags & (SF_OFFSET | SF_START)) {
+ ++Overwrites;
+ } else {
+ CfgError (GetSourcePos (M->LI),
+ "Segment '%s' of type 'overwrite' requires either"
+ " 'Start' or 'Offset' attribute to be specified",
+ GetString (S->Name));
+ }
+ } else {
+ if (Overwrites > 0) {
+ CfgError (GetSourcePos (M->LI),
+ "Segment '%s' is preceded by at least one segment"
+ " of type 'overwrite'",
+ GetString (S->Name));
+ }
+ }
+
+ /* Some actions depend on whether this is the load or run memory
+ ** area.
+ */
+ if (S->Run == M) {
+ /* This is the run (and maybe load) memory area. Handle
+ ** alignment and explict start address and offset.
+ */
+
+ /* Check if the alignment for the segment from the linker
+ ** config is a multiple for that of the segment.
+ ** If START or OFFSET is provided instead of ALIGN, check
+ ** if its address fits alignment requirements.
+ */
+ unsigned long AlignedBy = (S->Flags & SF_START) ? S->Addr
+ : (S->Flags & SF_OFFSET) ? (S->Addr + M->Start)
+ : S->RunAlignment;
+ if ((AlignedBy % S->Seg->Alignment) != 0) {
+ /* Segment requires another alignment than configured
+ ** in the linker.
+ */
+ CfgWarning (GetSourcePos (S->LI),
+ "Segment '%s' isn't aligned properly; the"
+ " resulting executable might not be functional.",
+ GetString (S->Name));
+ }
+
+ if (S->Flags & SF_ALIGN) {
+ /* Align the address */
+ unsigned long NewAddr = AlignAddr (Addr, S->RunAlignment);
+
+ /* If the first segment placed in the memory area needs
+ ** fill bytes for the alignment, emit a warning, since
+ ** that is somewhat suspicious.
+ */
+ if (M->FillLevel == 0 && NewAddr > Addr) {
+ CfgWarning (GetSourcePos (S->LI),
+ "The first segment in memory area '%s' "
+ "needs fill bytes for alignment.",
+ GetString (M->Name));
+ }
+
+ /* Use the aligned address */
+ Addr = NewAddr;
+
+ } else if ((S->Flags & (SF_OFFSET | SF_START)) != 0 &&
+ (M->Flags & MF_OVERFLOW) == 0) {
+ /* Give the segment a fixed starting address */
+ unsigned long NewAddr = S->Addr;
+
+ if (S->Flags & SF_OFFSET) {
+ /* An offset was given, no address, make an address */
+ NewAddr += M->Start;
+ }
+
+ if (S->Flags & SF_OVERWRITE) {
+ if (NewAddr < M->Start) {
+ CfgError (GetSourcePos (S->LI),
+ "Segment '%s' begins before memory area '%s'",
+ GetString (S->Name), GetString (M->Name));
+ } else {
+ Addr = NewAddr;
+ }
+ } else {
+ if (NewAddr < Addr) {
+ /* Offset already too large */
+ ++Overflows;
+ if (S->Flags & SF_OFFSET) {
+ CfgWarning (GetSourcePos (S->LI),
+ "Segment '%s' offset is too small in '%s' by %lu byte%c",
+ GetString (S->Name), GetString (M->Name),
+ Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's');
+ } else {
+ CfgWarning (GetSourcePos (S->LI),
+ "Segment '%s' start address is too low in '%s' by %lu byte%c",
+ GetString (S->Name), GetString (M->Name),
+ Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's');
+ }
+ } else {
+ Addr = NewAddr;
+ }
+ }
+ }
+
+ /* Set the start address of this segment, set the readonly flag
+ ** in the segment, and remember if the segment is in a
+ ** relocatable file or not.
+ */
+ S->Seg->PC = Addr;
+ S->Seg->ReadOnly = (S->Flags & SF_RO) != 0;
+
+ /* Remember the run memory for this segment, which is also a
+ ** flag that the segment has been placed.
+ */
+ S->Seg->MemArea = M;
+
+ } else if (S->Load == M) {
+ /* This is the load memory area; *and*, run and load are
+ ** different (because of the "else" above). Handle alignment.
+ */
+ if (S->Flags & SF_ALIGN_LOAD) {
+ /* Align the address */
+ Addr = AlignAddr (Addr, S->LoadAlignment);
+ }
+ }
+
+ /* If this is the load memory area, and the segment doesn't have a
+ ** fill value defined, use the one from the memory area.
+ */
+ if (S->Load == M && (S->Flags & SF_FILLVAL) == 0) {
+ S->Seg->FillVal = M->FillVal;
+ }
+
+ /* Increment the fill level of the memory area; and, check for an
+ ** overflow.
+ */
+ M->FillLevel = Addr + S->Seg->Size - M->Start;
+ if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) {
+ ++Overflows;
+ M->Flags |= MF_OVERFLOW;
+ CfgWarning (GetSourcePos (M->LI),
+ "Segment '%s' overflows memory area '%s' by %lu byte%c",
+ GetString (S->Name), GetString (M->Name),
+ M->FillLevel - M->Size, (M->FillLevel - M->Size == 1) ? ' ' : 's');
+ }
+
+ /* If requested, define symbols for the start and size of the
+ ** segment.
+ */
+ if (S->Flags & SF_DEFINE) {
+ if (S->Run == M && (S->Flags & SF_RUN_DEF) == 0) {
+ CreateRunDefines (S, Addr);
+ }
+ if (S->Load == M && (S->Flags & SF_LOAD_DEF) == 0) {
+ CreateLoadDefines (S, Addr);
+ }
+ }
+
+ /* Calculate the new address */
+ Addr += S->Seg->Size;
+
+ /* If this segment will go out to the file, or its place
+ ** in the file will be filled, then increase the file size.
+ */
+ if (S->Load == M &&
+ ((S->Flags & SF_BSS) == 0 || (M->Flags & MF_FILL) != 0)) {
+ M->F->Size += Addr - StartAddr;
+ }
+ }
+
+ /* If requested, define symbols for start, size, and offset of the
+ ** memory area
+ */
+ if (M->Flags & MF_DEFINE) {
+ Export* E;
+ StrBuf Buf = STATIC_STRBUF_INITIALIZER;
+
+ /* Define the size of the memory area */
+ SB_Printf (&Buf, "__%s_SIZE__", GetString (M->Name));
+ E = CreateConstExport (GetStrBufId (&Buf), M->Size);
+ CollAppend (&E->DefLines, M->LI);
+
+ /* Define the fill level of the memory area */
+ SB_Printf (&Buf, "__%s_LAST__", GetString (M->Name));
+ E = CreateMemoryExport (GetStrBufId (&Buf), M, M->FillLevel);
+ CollAppend (&E->DefLines, M->LI);
+
+ /* Define the file offset of the memory area. This isn't of much
+ ** use for relocatable output files.
+ */
+ if (!M->Relocatable) {
+ SB_Printf (&Buf, "__%s_FILEOFFS__", GetString (M->Name));
+ E = CreateConstExport (GetStrBufId (&Buf), M->FileOffs);
+ CollAppend (&E->DefLines, M->LI);
+ }
+
+ /* Throw away the string buffer */
+ SB_Done (&Buf);
+ }
+
+ /* If we didn't have an overflow, and are requested to fill the memory
+ ** area, account for that in the file size.
+ */
+ if ((M->Flags & MF_OVERFLOW) == 0 && (M->Flags & MF_FILL) != 0) {
+ M->F->Size += (M->Size - M->FillLevel);
+ }