fix(assembler): handle \x hex escapes and # inside quoted strings in .ascii

The internal assembler had two bugs in .ascii directive parsing:

1. Comment stripping treated '#' as a comment start even inside quoted
   strings, silently truncating string literals containing '#'. This
   corrupted .rodata for any program with '#' in string constants
   (e.g. QBE IR format strings like "# String literals").

2. The \xHH hex escape syntax was not handled. Bytes encoded as \xE2
   etc. were emitted as literal ASCII 'xE2' (3 bytes) instead of the
   single byte 0xE2, corrupting UTF-8 string data.

Both bugs were invisible when using the external assembler (gcc) and
only manifested when --assembler internal was used on large programs
with non-ASCII string literals or '#' characters in strings.
This commit is contained in:
Graeme Geldenhuys 2026-06-19 14:32:42 +01:00
parent fb0baa229f
commit dc19f39620

View file

@ -608,17 +608,35 @@ begin
S := TrimStr(ALine);
if (Length(S) = 0) or (S[0]= Ord('#')) then Exit;
{ Strip trailing comment }
{ Strip trailing comment, but skip over quoted strings so that '#'
inside .ascii "..." is not mistaken for a comment start. }
P := 0;
while P < Length(S) do
begin
if S[P]= Ord('#') then
if S[P] = Ord('"') then
begin
P := P + 1;
while P < Length(S) do
begin
if S[P] = Ord('\') then
P := P + 2
else if S[P] = Ord('"') then
begin
P := P + 1;
break;
end
else
P := P + 1;
end;
end
else if S[P] = Ord('#') then
begin
S := Copy(S, 0, P);
S := TrimStr(S);
break;
end;
P := P + 1;
end
else
P := P + 1;
end;
if Length(S) = 0 then Exit;
@ -2387,6 +2405,29 @@ begin
else if C = Ord('0') then Buf := Buf + #0
else if C = '\' then Buf := Buf + '\'
else if C = Ord('"') then Buf := Buf + '"'
else if (C = Ord('x')) or (C = Ord('X')) then
begin
{ Hex escape: \xHH — 1 or 2 hex digits }
Val := 0;
if (I + 1 < Len) and IsHexDigit(Args[I + 1]) then
begin
I := I + 1;
C := Args[I];
if IsDigit(C) then Val := Ord(C) - Ord('0')
else if (C >= Ord('a')) and (C <= Ord('f')) then Val := 10 + Ord(C) - Ord('a')
else Val := 10 + Ord(C) - Ord('A');
if (I + 1 < Len) and IsHexDigit(Args[I + 1]) then
begin
I := I + 1;
C := Args[I];
Val := Val * 16;
if IsDigit(C) then Val := Val + Ord(C) - Ord('0')
else if (C >= Ord('a')) and (C <= Ord('f')) then Val := Val + 10 + Ord(C) - Ord('a')
else Val := Val + 10 + Ord(C) - Ord('A');
end;
end;
Buf := Buf + Chr(Integer(Val));
end
else if IsDigit(C) then
begin
{ Octal escape }