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