Code cleanup from last patch, also, version bump for same. :-)
[rmac] / mark.c
1 //
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // MARK.C - A record of things that are defined relative to any of the sections
4 // Copyright (C) 199x Landon Dyer, 2011-2012 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source utilised with the kind permission of Landon Dyer
7 //
8
9 #include "mark.h"
10 #include "error.h"
11 #include "object.h"
12 #include "riscasm.h"
13 #include "sect.h"
14
15
16 #define MARK_ALLOC_INCR 1024            // # bytes to alloc for more mark space
17 #define MIN_MARK_MEM    (3 * sizeof(uint16_t) + 1 * sizeof(uint32_t) + sizeof(SYM *))
18
19 MCHUNK * firstmch;              // First mark chunk
20 MCHUNK * curmch;                // Current mark chunk
21 PTR markptr;                    // Deposit point in current mark chunk
22 uint32_t mcalloc;               // # bytes alloc'd to current mark chunk
23 uint32_t mcused;                // # bytes used in current mark chunk
24 uint16_t curfrom;               // Current "from" section
25
26 // Table to convert from TDB to fixup triad
27 static uint8_t mark_tr[] = {
28         0,              // (N/A)
29         2,              // TEXT relocatable
30         1, 0,   // DATA relocatable
31         3               // BSS relocatable
32 };
33
34 //#define DEBUG_IMAGE_MARKING
35
36
37 //
38 // Initialize marker
39 //
40 void InitMark(void)
41 {
42         firstmch = curmch = NULL;
43         mcalloc = mcused = 0;
44         curfrom = 0;
45         sect[TEXT].relocs = sect[DATA].relocs = sect[BSS].relocs = 0;
46 }
47
48
49 //
50 // Wrap up marker (called after final mark is made)
51 //
52 void StopMark(void)
53 {
54         if (curmch)
55         {
56                 *markptr.wp = MCHEND;           // Mark end of block
57                 curmch->mcused = mcused;        // Update # used in mark block
58         }
59 }
60
61
62 //
63 // Mark a word or longword as relocatable
64 //
65 // Record is either 2, 3, or 4 pieces of data long. A mark is of the form:
66 // .W    <to+flags>     section mark is relative to, and flags in upper byte
67 // .L    <loc>          location of mark in "from" section
68 // .W    [from]         new from section (if different from current)
69 // .L    [symbol]       symbol involved in external reference (if any)
70 //
71 uint32_t MarkRelocatable(uint16_t section, uint32_t loc, uint16_t to, uint16_t flags, SYM * symbol)
72 {
73 #ifdef DEBUG_IMAGE_MARKING
74 printf("MarkRelocatable: section=%i, loc=$%X, to=$%X, flags=$%x, symbol=$%X\n", section, loc, to, flags, symbol);
75 if (symbol)
76         printf("      symbol->stype=$%02X, sattr=$%04X, sattre=$%08X, svalue=%i, sname=%s\n", symbol->stype, symbol->sattr, symbol->sattre, symbol->svalue, symbol->sname);
77 #endif
78
79         if ((mcalloc - mcused) < MIN_MARK_MEM)
80                 AllocateMark();
81
82         // Set up flags
83         flags |= to;
84
85         if (section != curfrom)
86                 flags |= MCHFROM;
87
88         if (symbol != NULL)
89                 flags |= MSYMBOL;
90
91         //
92         // Complain about some things are not allowed in '-p' (PRG) mode:
93         //  o Marks that aren't to LONGs
94         //  o External references
95         //
96         if (prg_flag)
97         {
98 #if 0
99                 if ((flags & MLONG) == 0)
100                         error("illegal word relocatable (in .PRG mode)");
101 #endif
102
103                 if (symbol != NULL)
104                         errors("illegal external reference (in .PRG mode) to '%s'",
105                                 symbol->sname);
106         }
107
108         // Dump crap into the mark
109         *markptr.wp++ = flags;
110         *markptr.lp++ = loc;
111         mcused += sizeof(uint16_t) + sizeof(uint32_t);
112
113         if (flags & MCHFROM)
114         {
115                 curfrom = section;
116                 *markptr.wp++ = section;
117                 mcused += sizeof(uint16_t);
118         }
119
120         if (flags & MSYMBOL)
121         {
122                 *markptr.sy++ = symbol;
123                 mcused += sizeof(SYM *);
124         }
125
126         // Increment # of relocs in this section
127         sect[section].relocs++;
128
129         // Not sure what this is about (making sure the next mark is clear until
130         // it's marked as the end--I think)...
131         *markptr.wp = 0x0000;
132
133         return 0;
134 }
135
136
137 //
138 // Allocate another chunk of mark space
139 //
140 uint32_t AllocateMark(void)
141 {
142         // Alloc mark block header (and data) and set it up.
143         MCHUNK * p = malloc(sizeof(MCHUNK) + MARK_ALLOC_INCR);
144         p->mcnext = NULL;
145         p->mcalloc = MARK_ALLOC_INCR;
146         p->mcptr.cp = (uint8_t *)p + sizeof(MCHUNK);
147         p->mcused = 0;
148
149         if (firstmch == NULL)
150                 firstmch = p;
151
152         if (curmch)
153         {
154                 // Link onto previous chunk
155                 *markptr.wp++ = MCHEND;         // Mark end of block
156                 curmch->mcused = mcused;
157                 curmch->mcnext = p;
158         }
159
160         // Setup global vars
161         curmch = p;
162         markptr = p->mcptr;
163         mcalloc = MARK_ALLOC_INCR;
164         mcused = 0;
165
166         return 0;
167 }
168
169
170 //
171 // Make mark image for Alcyon .o file
172 // okflag: 1, ok to deposit reloc information
173 //
174 uint32_t MarkImage(register uint8_t * mp, uint32_t siz, uint32_t tsize, int okflag)
175 {
176         uint16_t from = 0;              // Section fixups are currently FROM
177         uint32_t loc;                   // Location (temp)
178         uint32_t lastloc;               // Last location fixed up (RELMOD)
179         uint8_t * wp;                   // Pointer into raw relocation information
180         register uint8_t * dp;  // Deposit point for RELMOD information
181
182         if (okflag)
183                 memset(mp, 0, siz);             // zero relocation buffer
184
185         for(MCHUNK * mch=firstmch; mch!=NULL; mch=mch->mcnext)
186         {
187                 for(PTR p=mch->mcptr;;)
188                 {
189                         uint16_t w = *p.wp++;// w = next mark entry
190
191                         if (w & MCHEND)         // (end of mark chunk)
192                                 break;
193
194                         // Get mark record
195                         SYM * symbol = NULL;
196                         loc = *p.lp++;          // mark location
197
198                         if (w & MCHFROM)        // maybe change "from" section
199                                 from = *p.wp++;
200
201                         if (w & MSYMBOL)        // maybe includes a symbol
202                                 symbol = *p.sy++;
203
204                         // Compute mark position in relocation information; in RELMOD mode,
205                         // get address of data to fix up.
206                         if (from == DATA)
207                                 loc += tsize;
208
209                         wp = (uint8_t *)(mp + loc);
210
211                         if (okflag && (w & MLONG)) // indicate first word of long
212                         {
213                                 wp[1] = 5;
214                                 wp += 2;
215                         }
216
217                         if (symbol)
218                         {
219                                 // Deposit external reference
220                                 if (okflag)
221                                 {
222                                         if (w & MPCREL)
223                                                 w = 6;          // PC-relative fixup
224                                         else
225                                                 w = 4;          // Absolute fixup
226
227                                         w |= symbol->senv << 3;
228                                         *wp++ = w >> 8;
229                                         *wp = w;
230                                 }
231                         }
232                         else
233                         {
234                                 // Deposit section-relative mark; in RELMOD mode, fix it up in
235                                 // the chunk, kind of like a sleazoid linker.
236                                 //
237                                 // In RELMOD mode, marks to words (MWORDs) "cannot happen,"
238                                 // checks are made when mark() is called, so we don't have to
239                                 // check again here.
240                                 w &= TDB;
241
242                                 if (okflag)
243                                         wp[1] = mark_tr[w];
244                                 else if (prg_flag && (w & (DATA | BSS)))
245                                 {
246                                         uint32_t diff = GETBE32(wp, 0);
247 #ifdef DO_DEBUG
248                                         DEBUG printf("diff=%lx ==> ", diff);
249 #endif
250                                         diff += sect[TEXT].sloc;
251
252                                         if (w == BSS)
253                                                 diff += sect[DATA].sloc;
254
255                                         SETBE32(wp, 0, diff)
256 #ifdef DO_DEBUG
257                                         DEBUG printf("%lx\n", diff);
258 #endif
259                                 }
260                         }
261                 }
262         }
263
264         // Generate ".PRG" relocation information in place in the relocation words
265         // (the "RELMOD" operation).
266         if (okflag && prg_flag)
267         {
268                 int firstp = 1;
269                 wp = dp = mp;
270
271                 for(loc=0; loc<siz;)
272                 {
273                         if ((wp[1] & 7) == 5)
274                         {
275                                 if (firstp)
276                                 {
277                                         SETBE32(dp, 0, loc);
278                                         dp += 4;
279                                         firstp = 0;
280                                 }
281                                 else
282                                 {
283                                         uint32_t diff;
284
285                                         for(diff=loc-lastloc; diff>254; diff-=254)
286                                                 *dp++ = 1;
287
288                                         *dp++ = (uint8_t)diff;
289                                 }
290
291                                 lastloc = loc;
292                                 loc += 4;
293                                 wp += 4;
294                         }
295                         else
296                         {
297                                 loc += 2;
298                                 wp += 2;
299                         }
300                 }
301
302                 // Terminate relocation list with 0L (if there was no relocation) or
303                 // 0.B (if relocation information was written).
304                 if (!firstp)
305                         *dp++ = 0;
306                 else
307                         for(firstp=0; firstp<4; firstp++)
308                                 *dp++ = 0;
309
310                 // Return size of relocation information
311                 loc = dp - mp;
312                 return loc;
313         }
314
315         return siz;
316 }
317
318
319 //
320 // Make mark image for BSD .o file
321 //
322 // Assumptions about mark records (for BSD): if there is a symbol, the mark is
323 // for an undefined symbol, otherwise it's just a normal TDB relocation.
324 // N.B.: tsize is only used if reqseg is DATA
325 //
326 uint32_t MarkBSDImage(uint8_t * mp, uint32_t siz, uint32_t tsize, int reqseg)
327 {
328         uint16_t from = 0;                      // Section fixups are currently FROM
329         uint32_t rsize = 0;                     // Relocation table size (written to mp)
330         int validsegment = 0;           // We are not yet in a valid segment...
331
332 #ifdef DEBUG_IMAGE_MARKING
333 printf("MarkBSDImage():\n");
334 #endif
335         // Initialize relocation table point (for D_foo macros)
336         chptr = mp;
337
338         // Run through all the relocation mark chunks
339         for(MCHUNK * mch=firstmch; mch!=NULL; mch=mch->mcnext)
340         {
341                 for(PTR p=mch->mcptr;;)
342                 {
343                         SYM * symbol = NULL;
344                         uint16_t w = *p.wp++;   // Next mark entry
345
346                         // If we hit the end of a chunk, go get the next one
347                         if (w & MCHEND)
348                                 break;
349
350                         // Get the rest of the mark record
351                         uint32_t loc = *p.lp++; // Mark location
352
353                         // Maybe change "from" section
354                         if (w & MCHFROM)
355                         {
356                                 from = *p.wp++;
357
358                                 if (((reqseg == TEXT) && (from == TEXT))
359                                         || ((reqseg == DATA) && (from == DATA)))
360                                         validsegment = 1;
361                                 else
362                                         validsegment = 0;
363                         }
364
365                         // Maybe includes a symbol
366                         if (w & MSYMBOL)
367                                 symbol = *p.sy++;
368
369                         if (!validsegment)
370                                 continue;
371
372 #ifdef DEBUG_IMAGE_MARKING
373 printf(" validsegment: raddr = $%08X\n", loc);
374 #endif
375                         uint32_t rflag = 0x00000040;    // Absolute relocation
376
377                         if (w & MPCREL)
378                                 rflag = 0x000000A0;                     // PC-relative relocation
379
380                         // This flag tells the linker to WORD swap the LONG when doing the
381                         // relocation.
382                         if (w & MMOVEI)
383                                 rflag |= 0x00000001;
384
385                         if (symbol != NULL)
386                         {
387                                 // Deposit external reference
388                                 rflag |= 0x00000010;                    // Set external reloc flag bit
389                                 rflag |= (symbol->senv << 8);   // Put symbol index in flags
390
391 #ifdef DEBUG_IMAGE_MARKING
392 printf("  validsegment(2): rflag = $%08X\n", rflag);
393 #endif
394                         }
395                         else
396                         {
397 #ifdef DEBUG_IMAGE_MARKING
398 printf("  w = $%04X\n", w);
399 #endif
400                                 w &= TDB;                               // Set reloc flags to segment
401
402                                 switch (w)
403                                 {
404                                 case TEXT: rflag |= 0x00000400; break;
405                                 case DATA: rflag |= 0x00000600; break;
406                                 case BSS:  rflag |= 0x00000800; break;
407                                 }
408
409 #ifdef DEBUG_IMAGE_MARKING
410 printf("  validsegment(3): rflag = $%08X\n", rflag);
411 #endif
412                                 // Fix relocation by adding in start of TEXT segment, since it's
413                                 // currently relative to the start of the DATA (or BSS) segment
414                                 if (w & (DATA | BSS))
415                                 {
416                                         uint8_t * dp = objImage + BSDHDRSIZE + loc;
417
418                                         // Bump the start of the section if it's DATA (& not TEXT)
419                                         if (from == DATA)
420                                                 dp += tsize;
421
422                                         uint32_t diff = GETBE32(dp, 0);
423                                         DEBUG printf("diff=%uX ==> ", diff);
424 #ifdef DEBUG_IMAGE_MARKING
425 printf("  validsegment(4): diff = $%08X --> ", diff);
426 #endif
427                                         if (rflag & 0x01)
428                                                 diff = WORDSWAP32(diff);
429
430                                         diff += sect[TEXT].sloc;
431
432                                         if (w == BSS)
433                                                 diff += sect[DATA].sloc;
434
435                                         if (rflag & 0x01)
436                                                 diff = WORDSWAP32(diff);
437
438                                         SETBE32(dp, 0, diff);
439                                         DEBUG printf("%uX\n", diff);
440 #ifdef DEBUG_IMAGE_MARKING
441 printf("$%08X\n", diff);
442 #endif
443                                 }
444                         }
445
446                         D_long(loc);            // Write relocation address
447                         D_long(rflag);          // Write relocation flags
448                         rsize += 0x08;          // Increment relocation size
449                 }
450         }
451
452         // Return relocation table's size
453 #ifdef DEBUG_IMAGE_MARKING
454 printf("  rsize = $%X\n", rsize);
455 #endif
456         return rsize;
457 }
458
459
460 //
461 // Make relocation record for ELF .o file.
462 // Returns the size of the relocation record.
463 //
464 uint32_t CreateELFRelocationRecord(uint8_t * buf, uint8_t * secBuf, uint16_t section)
465 {
466         uint16_t from = 0;              // Section fixups are currently FROM
467         uint32_t rsize = 0;             // Size of the relocation table
468
469         // Setup pointer for D_long/word/byte macros
470         chptr = buf;
471
472         for(MCHUNK * mch=firstmch; mch!=NULL; mch=mch->mcnext)
473         {
474                 for(register PTR p=mch->mcptr;;)
475                 {
476                         register uint16_t w = *p.wp++;  // w = next mark entry
477
478                         if (w & MCHEND)         // (end of mark chunk)
479                                 break;
480
481                         // Get mark record
482                         SYM * symbol = NULL;
483                         uint16_t symFlags = 0;
484                         uint32_t r_offset = *p.lp++;    // Mark's location
485
486                         if (w & MCHFROM)                // Maybe change "from" section
487                                 from = *p.wp++;
488
489                         if (w & MSYMBOL)                // Maybe includes a symbol
490                         {
491                                 symbol = *p.sy++;
492
493                                 if (symbol)
494                                         symFlags = symbol->sattr;
495                         }
496
497                         // Create relocation record for ELF object, if the mark is in the
498                         // current section.
499                         if (from & section)
500                         {
501                                 uint32_t r_sym = 0;
502                                 uint32_t r_type = 0;
503                                 uint32_t r_addend = 0;
504
505                                 // Since we're chucking all symbols here for ELF objects by
506                                 // default (cf. sect.c), we discriminate here (normally, if
507                                 // there is a symbol in the mark record, it means an undefined
508                                 // symbol) :-P
509                                 if (symbol && !(symFlags & DEFINED) && (symFlags & GLOBAL))
510                                         r_sym = symbol->senv + extraSyms;
511                                 else if (w & TEXT)
512                                         r_sym = elfHdrNum[ES_TEXT];     // Mark TEXT segment
513                                 else if (w & DATA)
514                                         r_sym = elfHdrNum[ES_DATA];     // Mark DATA segment
515                                 else if (w & BSS)
516                                         r_sym = elfHdrNum[ES_BSS];      // Mark BSS segment
517
518                                 // Set the relocation type next
519                                 if (w & MPCREL)
520                                         r_type = 5;  // R_68K_PC16
521                                 // N.B.: Since we've established that (from & section) is non-
522                                 //       zero, this condition will *never* be satisfied... :-P
523                                 //       It might be better to check the symbol's senv; that is,
524                                 //       if this is a real problem that needs addressing...
525                                 else if ((from & section) == 0)
526                                         // In the case of a section referring to a label in another
527                                         // section (for example text->data) use a R_68K_PC32 mark.
528                                         r_type = 4;  // R_68K_PC32
529                                 else
530                                         r_type = 1;  // R_68K_32
531
532                                 r_addend = GETBE32(secBuf + r_offset, 0);
533
534                                 // Deposit the relocation record
535                                 D_long(r_offset);
536                                 D_long((r_sym << 8) | r_type);
537                                 D_long(r_addend);
538                                 rsize += 0x0C;
539                         }
540                 }
541         }
542
543         return rsize;
544 }
545