2016-04-21 15:58:45 +03:00
|
|
|
// Copyright 2013,2016 Xamarin Inc. All rights reserved.
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
using System.Text;
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
|
|
using Xamarin.Utils;
|
|
|
|
|
|
|
|
namespace Xamarin.Bundler {
|
|
|
|
public class BitcodeConverter {
|
|
|
|
string input;
|
|
|
|
string outputFile;
|
|
|
|
Abi abi;
|
|
|
|
ApplePlatform platform;
|
|
|
|
Version deployment_target;
|
|
|
|
|
|
|
|
public BitcodeConverter (string input, string outputFile, ApplePlatform platform, Abi abi, Version deploymentTarget)
|
|
|
|
{
|
|
|
|
this.input = input;
|
|
|
|
this.outputFile = outputFile;
|
|
|
|
this.abi = abi;
|
|
|
|
this.platform = platform;
|
|
|
|
deployment_target = deploymentTarget;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Convert ()
|
|
|
|
{
|
|
|
|
Driver.Log (3, "Converting '{0}' to bitcode file {1}.", input, outputFile);
|
|
|
|
|
|
|
|
var reader = new StreamReader (input);
|
|
|
|
var writer = new StreamWriter (outputFile);
|
|
|
|
|
|
|
|
writer.WriteLine ("; ModuleID = '{0}'", input);
|
|
|
|
|
|
|
|
//This is for x86_64
|
|
|
|
switch (platform) {
|
|
|
|
case ApplePlatform.TVOS:
|
|
|
|
if ((abi & Abi.ARM64) != 0) {
|
|
|
|
writer.WriteLine ("target datalayout = \"e-m:o-i64:64-i128:128-n32:64-S128\"");
|
|
|
|
writer.Write ("target triple = \"arm64-apple-tvos");
|
|
|
|
} else if ((abi & Abi.x86_64) != 0) {
|
|
|
|
writer.WriteLine ("target datalayout = \"e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128\"");
|
|
|
|
//not 100% of this one (below)
|
|
|
|
writer.Write ("target triple = \"x86_64-apple-tvos");
|
|
|
|
} else {
|
|
|
|
throw ErrorHelper.CreateError (1301, "Unsupported TvOS ABI: {0}.", abi);
|
|
|
|
}
|
|
|
|
writer.Write (deployment_target.Major);
|
|
|
|
writer.Write ('.');
|
|
|
|
writer.Write (deployment_target.Minor);
|
|
|
|
writer.Write ('.');
|
|
|
|
writer.Write (deployment_target.Revision < 0 ? 0 : deployment_target.Revision);
|
|
|
|
writer.WriteLine ('"');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw ErrorHelper.CreateError (1300, "Unsupported bitcode platform: {0}.", platform);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
writer.WriteLine ("!llvm.module.flags = !{!0}");
|
|
|
|
writer.WriteLine ("!llvm.ident = !{!1}");
|
|
|
|
writer.WriteLine ("!0 = !{i32 1, !\"PIC Level\", i32 2}");
|
|
|
|
writer.WriteLine ("!1 = !{!\"Apple LLVM version 7.0.0 (clang-700.0.72)\"}");
|
|
|
|
writer.WriteLine ();
|
|
|
|
|
|
|
|
string s;
|
|
|
|
int line = 0;
|
|
|
|
while ((s = reader.ReadLine ()) != null) {
|
|
|
|
++line;
|
|
|
|
s = s.Trim ();
|
|
|
|
if (s.Length == 0)
|
|
|
|
continue;
|
|
|
|
|
2016-10-14 13:34:35 +03:00
|
|
|
if (s.StartsWith (".asciz", StringComparison.Ordinal) || s.StartsWith (".ascii", StringComparison.Ordinal))
|
2016-04-21 15:58:45 +03:00
|
|
|
s = FixAsciz (s, line);
|
2016-12-08 18:54:10 +03:00
|
|
|
else if (s.StartsWith (".file", StringComparison.Ordinal))
|
2016-11-18 09:02:41 +03:00
|
|
|
s = FixFile (s, line);
|
2016-04-21 15:58:45 +03:00
|
|
|
else if (s.Contains ("\""))
|
|
|
|
s = s.Replace ("\"", "\\22");
|
|
|
|
|
|
|
|
writer.WriteLine ("module asm \"{0}\"", s);
|
|
|
|
}
|
|
|
|
reader.Close ();
|
|
|
|
writer.Close ();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-06-01 18:24:39 +03:00
|
|
|
static bool IsDigit (byte c) {
|
2016-04-21 15:58:45 +03:00
|
|
|
return (c >= '0' && c <= '9');
|
|
|
|
}
|
|
|
|
|
2017-06-01 18:24:39 +03:00
|
|
|
static bool IsHex (byte c) {
|
2016-04-21 15:58:45 +03:00
|
|
|
return IsDigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
|
|
|
}
|
|
|
|
|
2017-06-01 18:24:39 +03:00
|
|
|
static bool ParseHex (byte[] str, ref int start_index, ref byte result)
|
2016-04-21 15:58:45 +03:00
|
|
|
{
|
|
|
|
int i = 0;
|
2017-06-01 18:24:39 +03:00
|
|
|
var sb = new StringBuilder ();
|
2016-04-21 15:58:45 +03:00
|
|
|
while (start_index + i < str.Length && i < 2) {
|
2017-06-01 18:24:39 +03:00
|
|
|
if (IsHex (str [start_index + i])) {
|
|
|
|
sb.Append ((char) str [start_index + i]);
|
2016-04-21 15:58:45 +03:00
|
|
|
++i;
|
2017-06-01 18:24:39 +03:00
|
|
|
} else
|
2016-04-21 15:58:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == 0)
|
|
|
|
return false;
|
|
|
|
|
2017-06-01 18:24:39 +03:00
|
|
|
result = System.Convert.ToByte (sb.ToString (), 16);
|
2016-04-21 15:58:45 +03:00
|
|
|
start_index += i - 1; //the main loop will skip the last character
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-01 18:24:39 +03:00
|
|
|
static bool ParseOctal (byte[] str, ref int start_index, ref byte result, ref string error_msg)
|
2016-04-21 15:58:45 +03:00
|
|
|
{
|
|
|
|
int i = 0;
|
2017-06-01 18:24:39 +03:00
|
|
|
var sb = new StringBuilder ();
|
2016-04-21 15:58:45 +03:00
|
|
|
while (start_index + i < str.Length && i < 3) {
|
2017-06-01 18:24:39 +03:00
|
|
|
if (IsDigit (str [start_index + i])) {
|
|
|
|
sb.Append ((char)str [start_index + i]);
|
2016-04-21 15:58:45 +03:00
|
|
|
++i;
|
2017-06-01 18:24:39 +03:00
|
|
|
} else
|
2016-04-21 15:58:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i != 3) {
|
2017-06-01 18:24:39 +03:00
|
|
|
error_msg = string.Format ("Column {0} expected 3 digits but got {1}, content is {2}", start_index, i, sb);
|
2016-04-21 15:58:45 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-01 18:24:39 +03:00
|
|
|
result = System.Convert.ToByte (sb.ToString (), 8);
|
2016-04-21 15:58:45 +03:00
|
|
|
start_index += i - 1; //the main loop will skip the last character
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
string FixAsciz (string s, int line) {
|
|
|
|
bool first = true;
|
|
|
|
var m = Regex.Match (s, "^.asci(i|z)\\s*\"(.*)\"$");
|
|
|
|
if (!m.Success)
|
|
|
|
return s;
|
|
|
|
var str = m.Groups [2].Value;
|
|
|
|
|
|
|
|
var res = new StringBuilder (str.Length * 3);
|
|
|
|
res.Append (".byte ");
|
|
|
|
/* it's a regular C string, parse time! */
|
2017-06-01 18:24:39 +03:00
|
|
|
var utf8 = Encoding.UTF8.GetBytes (str);
|
|
|
|
for (int i = 0; i < utf8.Length; ++i) {
|
|
|
|
byte to_append = 0;
|
|
|
|
if (utf8 [i] == '\\') {
|
2016-04-21 15:58:45 +03:00
|
|
|
++i;
|
2017-06-01 18:24:39 +03:00
|
|
|
if (i >= utf8.Length)
|
2016-04-21 15:58:45 +03:00
|
|
|
throw ErrorHelper.CreateError (1302, "Invalid escape sequence when converting .s to .ll, \\ as the last characted in {0}:{1}.", input, line);
|
2017-06-01 18:24:39 +03:00
|
|
|
switch (utf8 [i]) {
|
|
|
|
case (byte) 'b':
|
2016-04-21 15:58:45 +03:00
|
|
|
to_append = 0x8;
|
|
|
|
break;
|
2017-06-01 18:24:39 +03:00
|
|
|
case (byte)'f':
|
2016-04-21 15:58:45 +03:00
|
|
|
to_append = 0xc;
|
|
|
|
break;
|
2017-06-01 18:24:39 +03:00
|
|
|
case (byte)'n':
|
2016-04-21 15:58:45 +03:00
|
|
|
to_append = 0xa;
|
|
|
|
break;
|
2017-06-01 18:24:39 +03:00
|
|
|
case (byte)'r':
|
2016-04-21 15:58:45 +03:00
|
|
|
to_append = 0xd;
|
|
|
|
break;
|
2017-06-01 18:24:39 +03:00
|
|
|
case (byte)'t':
|
2016-04-21 15:58:45 +03:00
|
|
|
to_append = 9;
|
|
|
|
break;
|
2017-06-01 18:24:39 +03:00
|
|
|
case (byte)'\"':
|
|
|
|
case (byte)'\\':
|
|
|
|
to_append = utf8 [i];
|
2016-04-21 15:58:45 +03:00
|
|
|
break;
|
2017-06-01 18:24:39 +03:00
|
|
|
case (byte)'x':
|
|
|
|
case (byte)'X':
|
2016-04-21 15:58:45 +03:00
|
|
|
++i;
|
2017-06-01 18:24:39 +03:00
|
|
|
if (!ParseHex (utf8, ref i, ref to_append))
|
2016-04-21 15:58:45 +03:00
|
|
|
throw ErrorHelper.CreateError (1302, "Invalid escape sequence when converting .s to .ll, bad hex escape in {0}:{1}.", input, line);
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
2017-06-01 18:24:39 +03:00
|
|
|
if (IsDigit (utf8 [i])) {
|
2016-04-21 15:58:45 +03:00
|
|
|
string error_msg = null;
|
2017-06-01 18:24:39 +03:00
|
|
|
if (!ParseOctal (utf8, ref i, ref to_append, ref error_msg))
|
2016-04-21 15:58:45 +03:00
|
|
|
throw ErrorHelper.CreateError (1302, "Invalid escape sequence when converting .s to .ll, bad octal escape in {0}:{1} due to {2}.", input, line, error_msg);
|
|
|
|
} else
|
2017-06-01 18:24:39 +03:00
|
|
|
to_append = utf8 [i]; // "\K" is the same as "K"
|
2016-04-21 15:58:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2017-06-01 18:24:39 +03:00
|
|
|
to_append = utf8 [i];
|
2016-04-21 15:58:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!first)
|
|
|
|
res.Append (", ");
|
|
|
|
first = false;
|
|
|
|
res.Append (to_append.ToString ());
|
|
|
|
}
|
|
|
|
|
2016-10-14 13:34:35 +03:00
|
|
|
if (s.StartsWith (".asciz", StringComparison.Ordinal)) {
|
2016-04-21 15:58:45 +03:00
|
|
|
if (!first)
|
|
|
|
res.Append (", ");
|
|
|
|
res.Append ("0");
|
|
|
|
}
|
|
|
|
return res.ToString ();
|
|
|
|
}
|
2016-11-18 09:02:41 +03:00
|
|
|
|
|
|
|
string FixFile (string s, int line) {
|
|
|
|
var m = Regex.Match (s, "^.file\\s*(\\d+)\\s*\"(.*)\"$");
|
|
|
|
if (!m.Success)
|
|
|
|
return s;
|
|
|
|
var dbg_line = m.Groups [1].Value;
|
|
|
|
var str = m.Groups [2].Value;
|
|
|
|
|
|
|
|
var res = new StringBuilder (str.Length * 3);
|
|
|
|
res.Append (".file " + dbg_line + " " + "\\22");
|
|
|
|
|
|
|
|
for (int i = 0; i < str.Length; ++i) {
|
|
|
|
if (str [i] == '\\' && i + 1 < str.Length && str [i + 1] == '\\') {
|
|
|
|
i ++;
|
|
|
|
res.Append ("\\5c\\5c");
|
|
|
|
} else {
|
|
|
|
res.Append (str [i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res.Append ("\\22");
|
|
|
|
|
|
|
|
return res.ToString ();
|
|
|
|
}
|
2016-04-21 15:58:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|