@asmlift/core 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -24
- package/package.json +1 -1
- package/src/aggregate.ts +149 -0
- package/src/backend/c.ts +2 -0
- package/src/backend/cfamily.ts +197 -61
- package/src/backend/cpp.ts +3 -1
- package/src/backend/lines.ts +10 -0
- package/src/backend/pascal.ts +47 -16
- package/src/cdecl/parse.ts +21 -0
- package/src/contracts.ts +246 -36
- package/src/declare.ts +69 -20
- package/src/frontend/arm-body.ts +155 -0
- package/src/frontend/asmdata.ts +9 -2
- package/src/frontend/call-plan.ts +1117 -61
- package/src/frontend/callee-body.ts +370 -0
- package/src/frontend/dead-reads.ts +628 -0
- package/src/frontend/device-pins.ts +33 -4
- package/src/frontend/emit.ts +4 -3
- package/src/frontend/fpu.ts +224 -17
- package/src/frontend/frame-objects.ts +1689 -206
- package/src/frontend/frontend.ts +84 -3
- package/src/frontend/high-half.ts +53 -4
- package/src/frontend/jump-table.ts +545 -0
- package/src/frontend/local-object.ts +158 -32
- package/src/frontend/mips.ts +2296 -217
- package/src/frontend/own-struct-return.ts +500 -0
- package/src/frontend/ppc.ts +2748 -245
- package/src/frontend/register-struct-call.ts +399 -0
- package/src/frontend/reloc-symbol.ts +22 -7
- package/src/frontend/ssa.ts +1721 -43
- package/src/frontend/stackargs.ts +199 -72
- package/src/frontend/thumb-entry.ts +340 -0
- package/src/frontend/thumb.ts +2400 -325
- package/src/ir/alias.ts +65 -2
- package/src/ir/bits.ts +1699 -4
- package/src/ir/core.ts +254 -9
- package/src/ir/discipline.ts +86 -21
- package/src/ir/entry.ts +157 -0
- package/src/{structure → ir}/loops.ts +6 -2
- package/src/ir/opcodes.ts +137 -9
- package/src/ir/parse.ts +1 -0
- package/src/ir/simplify.ts +61 -10
- package/src/ir/types.ts +86 -2
- package/src/ir/verify.ts +96 -11
- package/src/l3/argbase.ts +2 -0
- package/src/l3/ast.ts +582 -18
- package/src/l3/basecse.ts +68 -15
- package/src/l3/caseendgoto.ts +223 -0
- package/src/l3/coalesce.ts +42 -14
- package/src/l3/constinit.ts +410 -0
- package/src/l3/dce.ts +53 -9
- package/src/l3/initfirst.ts +42 -11
- package/src/l3/intcell.ts +2 -1
- package/src/l3/narrowdecl.ts +77 -36
- package/src/l3/pollguard.ts +8 -2
- package/src/l3/ptrcell.ts +33 -34
- package/src/l3/ptrfield.ts +123 -8
- package/src/l3/regspell.ts +11 -4
- package/src/l3/reindex.ts +295 -99
- package/src/l3/scopebase.ts +2 -0
- package/src/l3/storage.ts +98 -1
- package/src/l3/symbol-refs.ts +84 -6
- package/src/l3/tailmerge.ts +6 -2
- package/src/l3/typing.ts +108 -15
- package/src/l3/unreduce.ts +6 -5
- package/src/l3/volatileptr.ts +17 -54
- package/src/mangle.ts +30 -0
- package/src/pattern/engine.ts +361 -73
- package/src/pipeline.ts +1039 -38
- package/src/proto-context.ts +1039 -45
- package/src/proto.ts +555 -40
- package/src/raise/aggmembers.ts +290 -0
- package/src/raise/arrays.ts +5 -1
- package/src/raise/const.ts +25 -0
- package/src/raise/declared-volatile.ts +207 -31
- package/src/raise/divpow2.ts +192 -0
- package/src/raise/floathelpers.ts +254 -33
- package/src/raise/gvn.ts +3 -3
- package/src/raise/latch.ts +162 -6
- package/src/raise/memberarrays.ts +10 -4
- package/src/raise/offsetnames.ts +422 -36
- package/src/raise/pairparams.ts +33 -0
- package/src/raise/paramwidth.ts +196 -6
- package/src/raise/pre-recovery.ts +107 -21
- package/src/raise/pre-structure.ts +73 -0
- package/src/raise/recover.ts +12 -5
- package/src/raise/reroll.ts +674 -0
- package/src/raise/shortcircuit.ts +217 -34
- package/src/raise/softdiv.ts +1 -1
- package/src/raise/storesign.ts +153 -0
- package/src/raise/struct-arrays.ts +30 -11
- package/src/raise/structs.ts +396 -33
- package/src/raise/truncload.ts +4 -3
- package/src/raise/unpre.ts +594 -0
- package/src/raise/unthread.ts +400 -0
- package/src/rank-declare.ts +118 -12
- package/src/rank-variations.ts +186 -35
- package/src/rank.ts +536 -68
- package/src/runtime-helpers.ts +140 -22
- package/src/structure/analysis.ts +217 -8
- package/src/structure/arith-ops.ts +23 -0
- package/src/structure/cross-jumped-tail.ts +216 -0
- package/src/structure/globalaccess.ts +157 -10
- package/src/structure/hazards.ts +16 -64
- package/src/structure/local-statics.ts +145 -9
- package/src/structure/namecoalesce.ts +1 -1
- package/src/structure/pointer-spelling.ts +1106 -0
- package/src/structure/redundant-test.ts +1 -1
- package/src/structure/structure.ts +6192 -1213
- package/src/structure/switch-recover.ts +583 -120
- package/src/structure/unfolded-branch-constant.ts +90 -0
- package/src/symbols.ts +58 -15
- package/src/target.ts +1028 -54
- package/src/trace.ts +49 -17
- package/src/variation-definitions.ts +333 -20
- package/src/variation-gates.ts +4 -0
- package/src/variation-tokens.ts +68 -3
|
@@ -16,8 +16,12 @@
|
|
|
16
16
|
// static — when:
|
|
17
17
|
// • the target does not carry the definition (the symbol is absent, or its label heads no data);
|
|
18
18
|
// • the object sits in a section that is not read-only data, initialized data or bss;
|
|
19
|
-
// • a relocation falls inside its bytes
|
|
20
|
-
//
|
|
19
|
+
// • a relocation falls inside its bytes that is no pointer-width absolute address of an ordinary
|
|
20
|
+
// global: one narrower or wider than a pointer, or off the object's words, or of another type,
|
|
21
|
+
// or naming what no declaration outside the function can (a section, the compiler's anonymous
|
|
22
|
+
// constant, another function-scope static — reloc-symbol.ts's kinds). Such an address is
|
|
23
|
+
// RECORDED (`LocalObject.relocs`) and left to the reader of the object to spell or refuse
|
|
24
|
+
// (structure/local-statics.ts);
|
|
21
25
|
// • its extent cannot be read: a directive inside its data run this reader does not parse, a
|
|
22
26
|
// size that disagrees with the bytes under the label, or an empty object;
|
|
23
27
|
// • its alignment cannot be read: an `.align` form this reader does not parse, or an object file
|
|
@@ -28,9 +32,14 @@
|
|
|
28
32
|
// re-declaring it inside one of them would split one object in two. Only the functions this
|
|
29
33
|
// input shows can be checked, which is the limit of this refusal: a single-function input
|
|
30
34
|
// cannot show a second referrer, and one that ends at this function cannot show a later one.
|
|
31
|
-
import
|
|
35
|
+
import {
|
|
36
|
+
type LocalObject,
|
|
37
|
+
type LocalObjectReloc,
|
|
38
|
+
type LocalObjects,
|
|
39
|
+
LOCAL_OBJECT_ADDRESS_BYTES as POINTER_BYTES,
|
|
40
|
+
} from '../ir/core';
|
|
32
41
|
import type { AsmData } from './asmdata';
|
|
33
|
-
import { localStaticName } from './reloc-symbol';
|
|
42
|
+
import { classifyRelocSymbol, localStaticName, unspellableReason } from './reloc-symbol';
|
|
34
43
|
|
|
35
44
|
/** A definition, or why there is none: the tail of a refusal sentence the caller opens with
|
|
36
45
|
* "names a function-scope static ('<symbol>')", so every refusal of the kind reads the same. */
|
|
@@ -38,6 +47,14 @@ export type LocalObjectRead = LocalObject | { refused: string };
|
|
|
38
47
|
|
|
39
48
|
const refused = (why: string): { refused: string } => ({ refused: why });
|
|
40
49
|
|
|
50
|
+
/** Why an initializer's address of `symbol` cannot be recorded, as the tail of the refusal
|
|
51
|
+
* sentence — or null for an ordinary global's, which a declaration outside the function names.
|
|
52
|
+
* Every other kind is the naming policy's own (reloc-symbol.ts), with its own sentence. */
|
|
53
|
+
function addressRefusal(symbol: string): string | null {
|
|
54
|
+
const why = unspellableReason(symbol);
|
|
55
|
+
return why === null ? null : `whose initializer holds an address that ${why}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
41
58
|
// ── GNU as text (agbcc) ──────────────────────────────────────────────────────────────────────────
|
|
42
59
|
|
|
43
60
|
/** A line without its `@` comment, where the `@` is not inside a string literal. */
|
|
@@ -112,18 +129,22 @@ const le = (value: number, width: number): number[] =>
|
|
|
112
129
|
Array.from({ length: width }, (_, i) => (value >>> (8 * i)) & 0xff);
|
|
113
130
|
|
|
114
131
|
/** What one data directive writes: its bytes, the width of each value it writes (null for
|
|
115
|
-
* padding, which writes none), whether one of those values is negative,
|
|
116
|
-
* string. */
|
|
132
|
+
* padding, which writes none), whether one of those values is negative, whether it writes a
|
|
133
|
+
* string, and the addresses among its values, at their byte offsets in the directive. */
|
|
117
134
|
interface DirectiveData {
|
|
118
135
|
bytes: number[];
|
|
119
136
|
width: number | null;
|
|
120
137
|
negative: boolean;
|
|
121
138
|
string: boolean;
|
|
139
|
+
relocs: LocalObjectReloc[];
|
|
122
140
|
}
|
|
123
141
|
|
|
124
|
-
/**
|
|
125
|
-
*
|
|
126
|
-
|
|
142
|
+
/** `sym`, `sym+K` or `sym-K` — the operand GNU as relocates to a symbol's address plus a constant. */
|
|
143
|
+
const GAS_ADDRESS = /^([A-Za-z_.$][\w.$]*)\s*(?:([+-])\s*(\S+))?$/;
|
|
144
|
+
|
|
145
|
+
/** One data directive's data; `{ refused }` for an operand that is no number and no address this
|
|
146
|
+
* reader records, null when the directive is not a data directive this reader parses. */
|
|
147
|
+
function dataBytes(directive: string, operands: string): DirectiveData | { refused: string } | null {
|
|
127
148
|
const width = /^(byte)$/.test(directive)
|
|
128
149
|
? 1
|
|
129
150
|
: /^(short|hword|2byte)$/.test(directive)
|
|
@@ -133,29 +154,54 @@ function dataBytes(directive: string, operands: string): DirectiveData | { addre
|
|
|
133
154
|
: 0;
|
|
134
155
|
if (width > 0) {
|
|
135
156
|
const out: number[] = [];
|
|
157
|
+
const relocs: LocalObjectReloc[] = [];
|
|
136
158
|
let negative = false;
|
|
137
159
|
for (const op of operands.split(',')) {
|
|
138
160
|
const v = gasInteger(op);
|
|
139
|
-
if (v
|
|
140
|
-
|
|
161
|
+
if (v !== null) {
|
|
162
|
+
negative ||= v < 0;
|
|
163
|
+
out.push(...le(v, width));
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// Not a number, so the assembler relocates the value: an address, which the bytes do not hold.
|
|
167
|
+
const a = op.trim().match(GAS_ADDRESS);
|
|
168
|
+
// agbcc writes a negative addend `garr+-0x4` (compiled: `&garr[-1]`), so the constant keeps a sign
|
|
169
|
+
const k = a === null ? null : a[3] === undefined ? 0 : gasInteger(a[3]);
|
|
170
|
+
if (a === null || k === null) {
|
|
171
|
+
return refused(`whose initializer holds '${op.trim()}', an expression this reader does not read as an address`);
|
|
141
172
|
}
|
|
142
|
-
|
|
143
|
-
|
|
173
|
+
if (width !== POINTER_BYTES) {
|
|
174
|
+
return refused(
|
|
175
|
+
`whose initializer holds the address '${op.trim()}' in a ${width}-byte value — not a pointer's ${POINTER_BYTES}`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
const why = addressRefusal(a[1]);
|
|
179
|
+
if (why !== null) {
|
|
180
|
+
return refused(why);
|
|
181
|
+
}
|
|
182
|
+
relocs.push({ offset: out.length, symbol: a[1], addend: a[2] === '-' ? -k : k });
|
|
183
|
+
out.push(...le(0, width));
|
|
144
184
|
}
|
|
145
|
-
return { bytes: out, width, negative, string: false };
|
|
185
|
+
return { bytes: out, width, negative, string: false, relocs };
|
|
146
186
|
}
|
|
147
187
|
if (/^(space|skip|zero)$/.test(directive)) {
|
|
148
188
|
const [n, fill, ...rest] = operands.split(',').map((s) => gasInteger(s));
|
|
149
189
|
return n === null || n < 0 || rest.length > 0 || fill === null
|
|
150
190
|
? null
|
|
151
|
-
: {
|
|
191
|
+
: {
|
|
192
|
+
bytes: new Array<number>(n).fill((fill ?? 0) & 0xff),
|
|
193
|
+
width: null,
|
|
194
|
+
negative: false,
|
|
195
|
+
string: false,
|
|
196
|
+
relocs: [],
|
|
197
|
+
};
|
|
152
198
|
}
|
|
153
199
|
if (/^(ascii|asciz|string)$/.test(directive)) {
|
|
154
200
|
const m = operands.trim().match(/^"((?:[^"\\]|\\.)*)"$/);
|
|
155
201
|
const s = m ? gasString(m[1]) : null;
|
|
156
202
|
return s === null
|
|
157
203
|
? null
|
|
158
|
-
: { bytes: directive === 'ascii' ? s : [...s, 0], width: 1, negative: false, string: true };
|
|
204
|
+
: { bytes: directive === 'ascii' ? s : [...s, 0], width: 1, negative: false, string: true, relocs: [] };
|
|
159
205
|
}
|
|
160
206
|
return null;
|
|
161
207
|
}
|
|
@@ -212,6 +258,40 @@ export function readGasLocalObject(asm: string, symbol: string): LocalObjectRead
|
|
|
212
258
|
return refused('whose name has no source spelling this reader knows');
|
|
213
259
|
}
|
|
214
260
|
const { name, counter: order } = parts;
|
|
261
|
+
const read = readGasDefinition(asm, symbol);
|
|
262
|
+
return 'refused' in read ? read : { name, symbol, order, ...read };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** Read an ANONYMOUS constant out of GNU as text: the data run under a compiler-generated `.L`
|
|
266
|
+
* label (agbcc's `.LC0`), which is how agbcc 2.9 places the image a local aggregate's constant
|
|
267
|
+
* initializer is copied from (`u8 a[16] = {…}` is `ldr r1,=.LC0; mov r0,sp; mov r2,#16; bl memcpy`,
|
|
268
|
+
* varasm.c `output_constant_def`). Nothing in the source names it, so no declaration can spell it:
|
|
269
|
+
* the object is carried {@link LocalObject.anonymous} and spelled only as the initializer of the
|
|
270
|
+
* local it is copied into (l3/constinit.ts), or refused there.
|
|
271
|
+
*
|
|
272
|
+
* Read-only data only, with the same refusals as a static's definition: a `.L` label anywhere else
|
|
273
|
+
* is code, or data this reader has no reason to believe is a constant image. That gate is agbcc's
|
|
274
|
+
* placement, not every compiler's: IDO 7.1 places the same image in `.data` (compiled). Which
|
|
275
|
+
* names are anonymous is reloc-symbol.ts's decision (`anon-pool`). */
|
|
276
|
+
export function readGasAnonymousObject(asm: string, label: string): LocalObjectRead {
|
|
277
|
+
if (classifyRelocSymbol(label) !== 'anon-pool') {
|
|
278
|
+
return refused('whose label is not a compiler-generated `.L` label');
|
|
279
|
+
}
|
|
280
|
+
const read = readGasDefinition(asm, label);
|
|
281
|
+
if ('refused' in read) {
|
|
282
|
+
return read;
|
|
283
|
+
}
|
|
284
|
+
if (read.section !== 'rodata') {
|
|
285
|
+
return refused(`defined in ${read.section}, which is not the read-only data a constant image sits in`);
|
|
286
|
+
}
|
|
287
|
+
return { name: label, symbol: label, order: -1, anonymous: true, ...read };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** A definition's section, size, bytes and directives, read from the label down. */
|
|
291
|
+
function readGasDefinition(
|
|
292
|
+
asm: string,
|
|
293
|
+
symbol: string,
|
|
294
|
+
): Omit<LocalObject, 'name' | 'symbol' | 'order'> | { refused: string } {
|
|
215
295
|
const lines = asm.split('\n').map(code);
|
|
216
296
|
let declaredSize: number | null = null;
|
|
217
297
|
let section: string | null = null;
|
|
@@ -225,7 +305,7 @@ export function readGasLocalObject(asm: string, symbol: string): LocalObjectRead
|
|
|
225
305
|
if (size === null || size <= 0) {
|
|
226
306
|
return refused(`whose '.lcomm' size '${lcomm[2]}' is not a positive number`);
|
|
227
307
|
}
|
|
228
|
-
return {
|
|
308
|
+
return { section: 'bss', size, bigEndian: false };
|
|
229
309
|
}
|
|
230
310
|
const sz = line.match(/^\.size\s+([^\s,]+)\s*,\s*(\S+)$/);
|
|
231
311
|
if (sz && sz[1] === symbol) {
|
|
@@ -255,6 +335,8 @@ export function readGasLocalObject(asm: string, symbol: string): LocalObjectRead
|
|
|
255
335
|
const widths = new Set<number>();
|
|
256
336
|
let negative = false;
|
|
257
337
|
const strings = new Set<boolean>();
|
|
338
|
+
const gaps: [number, number][] = [];
|
|
339
|
+
const relocs: LocalObjectReloc[] = [];
|
|
258
340
|
const first = lines[at].replace(/^[A-Za-z_.$][\w.$]*:\s*/, '');
|
|
259
341
|
for (let i = at; i < lines.length; i++) {
|
|
260
342
|
const line = i === at ? first : lines[i];
|
|
@@ -275,8 +357,22 @@ export function readGasLocalObject(asm: string, symbol: string): LocalObjectRead
|
|
|
275
357
|
if (b === null) {
|
|
276
358
|
return refused(`whose data run holds '.${d[1]}', a directive this reader does not read as bytes`);
|
|
277
359
|
}
|
|
278
|
-
if ('
|
|
279
|
-
return
|
|
360
|
+
if ('refused' in b) {
|
|
361
|
+
return b;
|
|
362
|
+
}
|
|
363
|
+
// A word lands wherever the run has reached: agbcc aligns an object's words, never one word
|
|
364
|
+
// inside it, so one off a pointer boundary is no pointer of the object's.
|
|
365
|
+
if (b.relocs.length > 0 && bytes.length % POINTER_BYTES !== 0) {
|
|
366
|
+
return refused(`whose initializer holds the address '${b.relocs[0].symbol}' off its ${POINTER_BYTES}-byte words`);
|
|
367
|
+
}
|
|
368
|
+
relocs.push(...b.relocs.map((r) => ({ ...r, offset: r.offset + bytes.length })));
|
|
369
|
+
if (b.width === null && b.bytes.length > 0 && b.bytes.every((x) => x === 0)) {
|
|
370
|
+
const last = gaps[gaps.length - 1];
|
|
371
|
+
if (last !== undefined && last[0] + last[1] === bytes.length) {
|
|
372
|
+
last[1] += b.bytes.length;
|
|
373
|
+
} else {
|
|
374
|
+
gaps.push([bytes.length, b.bytes.length]);
|
|
375
|
+
}
|
|
280
376
|
}
|
|
281
377
|
bytes.push(...b.bytes);
|
|
282
378
|
if (b.width !== null) {
|
|
@@ -292,8 +388,8 @@ export function readGasLocalObject(asm: string, symbol: string): LocalObjectRead
|
|
|
292
388
|
return refused(`whose '.size' (${declaredSize}) disagrees with the ${bytes.length} bytes under its label`);
|
|
293
389
|
}
|
|
294
390
|
if (kind === 'bss') {
|
|
295
|
-
return bytes.every((b) => b === 0)
|
|
296
|
-
? {
|
|
391
|
+
return bytes.every((b) => b === 0) && relocs.length === 0
|
|
392
|
+
? { section: 'bss', size: bytes.length, bigEndian: false }
|
|
297
393
|
: refused('defined in bss with non-zero bytes');
|
|
298
394
|
}
|
|
299
395
|
const align = alignAhead(lines, at);
|
|
@@ -307,9 +403,6 @@ export function readGasLocalObject(asm: string, symbol: string): LocalObjectRead
|
|
|
307
403
|
// struct's mixed directives a negative belongs to one field, which says nothing of another.
|
|
308
404
|
const unit = widths.size === 1 && bytes.length % [...widths][0] === 0 ? [...widths][0] : undefined;
|
|
309
405
|
return {
|
|
310
|
-
name,
|
|
311
|
-
symbol,
|
|
312
|
-
order,
|
|
313
406
|
section: kind,
|
|
314
407
|
size: bytes.length,
|
|
315
408
|
bytes: Uint8Array.from(bytes),
|
|
@@ -319,7 +412,9 @@ export function readGasLocalObject(asm: string, symbol: string): LocalObjectRead
|
|
|
319
412
|
...(unit !== undefined ? { unit } : {}),
|
|
320
413
|
negative: unit !== undefined && negative,
|
|
321
414
|
string: strings.size === 1 && strings.has(true),
|
|
415
|
+
...(widths.size > 0 && gaps.length > 0 ? { gaps } : {}),
|
|
322
416
|
},
|
|
417
|
+
...(relocs.length > 0 ? { relocs } : {}),
|
|
323
418
|
};
|
|
324
419
|
}
|
|
325
420
|
|
|
@@ -382,15 +477,24 @@ const OBJECT_SECTIONS: Readonly<Record<string, LocalObject['section']>> = {
|
|
|
382
477
|
'.sbss': 'bss',
|
|
383
478
|
};
|
|
384
479
|
|
|
480
|
+
/** The relocations that fill a pointer-width word with a symbol's absolute address — the only kind
|
|
481
|
+
* an initializer's address constant is. Each is listed where a frontend reads its objects through
|
|
482
|
+
* this reader: mwcc's (frontend/ppc.ts); MIPS addresses a function-scope static section-relative
|
|
483
|
+
* and does not reach it. RELA types only: the addend is read off the relocation entry alone, and a
|
|
484
|
+
* REL type (whose addend is the word it fills) joins only with a reader of that word. */
|
|
485
|
+
const ABSOLUTE_WORD: ReadonlySet<string> = new Set(['R_PPC_ADDR32']);
|
|
486
|
+
|
|
385
487
|
/** Sections whose relocations describe the program for a debugger, not for the machine. */
|
|
386
488
|
const isDebugSection = (s: string): boolean => /^\.(debug|line)/.test(s);
|
|
387
489
|
|
|
388
|
-
/** mwcc's `.comment` section: `CodeWarrior
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
|
|
490
|
+
/** mwcc's `.comment` section: `CodeWarrior`, a version byte (0x0a from 2.4.2 and 2.4.7, 0x08 from
|
|
491
|
+
* 2.3.3), a header to 0x2c, then one 8-byte record per symbol-table entry, the null one first,
|
|
492
|
+
* whose first word is the alignment mwcc gave that symbol (compiled on all three builds: `s64`
|
|
493
|
+
* records 8 and lands at 8 after an `s32`, `aligned(32)` records 32, a scalar `u8` 1). No section
|
|
494
|
+
* header or symbol value says the same: a section's alignment is its widest object's, and an
|
|
495
|
+
* object at offset 0 of one is aligned to anything. The magic is the eleven letters alone, so the
|
|
496
|
+
* version byte never decides whether the records are read. */
|
|
497
|
+
const MW_COMMENT_MAGIC = 'CodeWarrior';
|
|
394
498
|
const MW_COMMENT_RECORDS = 0x2c;
|
|
395
499
|
|
|
396
500
|
/** The alignment mwcc's `.comment` records for the symbol at `index`, or null when the section is
|
|
@@ -432,9 +536,10 @@ export function readObjectLocalObject(ad: AsmData, symbol: string, fn: string):
|
|
|
432
536
|
return refused('whose symbol-table size is 0, so its extent is unknown');
|
|
433
537
|
}
|
|
434
538
|
const self = ad.symbols.get(fn);
|
|
539
|
+
const inside: typeof ad.relocs = [];
|
|
435
540
|
for (const r of ad.relocs) {
|
|
436
541
|
if (r.section === sym.section && r.offset >= sym.value && r.offset < sym.value + sym.size) {
|
|
437
|
-
|
|
542
|
+
inside.push(r);
|
|
438
543
|
}
|
|
439
544
|
if (r.sym !== symbol || isDebugSection(r.section)) {
|
|
440
545
|
continue;
|
|
@@ -451,12 +556,32 @@ export function readObjectLocalObject(ad: AsmData, symbol: string, fn: string):
|
|
|
451
556
|
}
|
|
452
557
|
const placement = { align, section: sym.section, offset: sym.value };
|
|
453
558
|
if (kind === 'bss') {
|
|
454
|
-
return
|
|
559
|
+
return inside.length === 0
|
|
560
|
+
? { name, symbol, order, section: 'bss', size: sym.size, bigEndian: ad.bigEndian, placement }
|
|
561
|
+
: refused('defined in bss with a relocation inside it');
|
|
455
562
|
}
|
|
456
563
|
const contents = ad.sections.get(sym.section);
|
|
457
564
|
if (contents === undefined || sym.value + sym.size > contents.length) {
|
|
458
565
|
return refused(`whose bytes the side table's '${sym.section}' contents do not hold`);
|
|
459
566
|
}
|
|
567
|
+
const relocs: LocalObjectReloc[] = [];
|
|
568
|
+
for (const r of inside.sort((a, b) => a.offset - b.offset)) {
|
|
569
|
+
const at = r.offset - sym.value;
|
|
570
|
+
if (!ABSOLUTE_WORD.has(r.type)) {
|
|
571
|
+
return refused(
|
|
572
|
+
`whose initializer holds the address '${r.sym}' by a ${r.type} relocation — no pointer's absolute address`,
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
if (at % POINTER_BYTES !== 0 || at + POINTER_BYTES > sym.size) {
|
|
576
|
+
return refused(`whose initializer holds the address '${r.sym}' off its ${POINTER_BYTES}-byte words`);
|
|
577
|
+
}
|
|
578
|
+
const why = addressRefusal(r.sym);
|
|
579
|
+
if (why !== null) {
|
|
580
|
+
return refused(why);
|
|
581
|
+
}
|
|
582
|
+
// a RELA relocation (ABSOLUTE_WORD): the linker replaces the word, so its bytes add nothing
|
|
583
|
+
relocs.push({ offset: at, symbol: r.sym, addend: r.addend | 0 });
|
|
584
|
+
}
|
|
460
585
|
return {
|
|
461
586
|
name,
|
|
462
587
|
symbol,
|
|
@@ -466,6 +591,7 @@ export function readObjectLocalObject(ad: AsmData, symbol: string, fn: string):
|
|
|
466
591
|
bytes: contents.slice(sym.value, sym.value + sym.size),
|
|
467
592
|
bigEndian: ad.bigEndian,
|
|
468
593
|
placement,
|
|
594
|
+
...(relocs.length > 0 ? { relocs } : {}),
|
|
469
595
|
};
|
|
470
596
|
}
|
|
471
597
|
|