mirror of
https://github.com/fluencelabs/musl
synced 2025-04-24 23:02:14 +00:00
Loading of shared libraries was broken back in: 39fde60 This change restores the ability for shared libraries to be loaded such that they can share memory and provide function exports to the main executable. However the executable that libraries still need to built in a specific way (i.e. all importing the same sized memory) and their static globals need to be in the different address range. This has the complication that the executables won't run the wasm spec interpreter (since it doesn't pass in a memory to the executables it runs). So although I'm fixing this here in the musl repo I'm also proposiing that we remove the continuous testing of this configurations on the wasm waterfall in favor of concentrating on the statically linking lld case: WebAssembly/waterfall#271
260 lines
8.7 KiB
Python
Executable File
260 lines
8.7 KiB
Python
Executable File
#! /usr/bin/env python
|
|
|
|
# Copyright 2016 WebAssembly Community Group participants
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import argparse
|
|
import glob
|
|
import itertools
|
|
import multiprocessing
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
|
|
verbose = False
|
|
|
|
DIR_BLACKLIST = ['misc', 'thread']
|
|
BLACKLIST = [
|
|
'puts.c', # Prefer the JS version for now
|
|
'abort.c', # Perfer the JS version for now
|
|
'_Exit.c', # Perfer the JS version for now
|
|
]
|
|
# Files that contain weak reference which are not suppoered by s2wasm
|
|
WEAK_BLACKLIST = [
|
|
'exit.c',
|
|
'__libc_start_main.c',
|
|
]
|
|
CFLAGS = ['-std=c99',
|
|
'-D_XOPEN_SOURCE=700',
|
|
'-Werror',
|
|
'-Wno-incompatible-library-redeclaration',
|
|
'-Wno-shift-op-parentheses',
|
|
'-Wno-tautological-unsigned-zero-compare',
|
|
'-Wno-tautological-constant-out-of-range-compare',
|
|
'-Wno-tautological-unsigned-enum-zero-compare',
|
|
'-Wno-ignored-attributes',
|
|
'-Wno-format',
|
|
'-Wno-bitwise-op-parentheses',
|
|
'-Wno-logical-op-parentheses',
|
|
'-Wno-string-plus-int',
|
|
'-Wno-pointer-sign',
|
|
'-Wno-dangling-else',
|
|
'-Wno-absolute-value',
|
|
'-Wno-unknown-pragmas']
|
|
|
|
|
|
def check_output(cmd, **kwargs):
|
|
cwd = kwargs.get('cwd', os.getcwd())
|
|
if verbose:
|
|
c = ' '.join('"' + c + '"' if ' ' in c else c for c in cmd)
|
|
print ' `%s`, cwd=`%s`' % (c, cwd)
|
|
return subprocess.check_output(cmd, cwd=cwd)
|
|
|
|
|
|
def change_extension(path, new_extension):
|
|
return path[:path.rfind('.')] + new_extension
|
|
|
|
|
|
def create_version(musl):
|
|
"""musl's Makefile creates version.h"""
|
|
script = os.path.join(musl, 'tools', 'version.sh')
|
|
version = check_output(['sh', script], cwd=musl).strip()
|
|
with open(os.path.join(musl, 'src', 'internal', 'version.h'), 'w') as v:
|
|
v.write('#define VERSION "%s"\n' % version)
|
|
|
|
|
|
def build_alltypes(musl, arch):
|
|
"""Emulate musl's Makefile build of alltypes.h."""
|
|
mkalltypes = os.path.join(musl, 'tools', 'mkalltypes.sed')
|
|
inbits = os.path.join(musl, 'arch', arch, 'bits', 'alltypes.h.in')
|
|
intypes = os.path.join(musl, 'include', 'alltypes.h.in')
|
|
out = check_output(['sed', '-f', mkalltypes, inbits, intypes])
|
|
with open(os.path.join(musl, 'arch', arch, 'bits', 'alltypes.h'), 'w') as o:
|
|
o.write(out)
|
|
|
|
|
|
def musl_sources(musl_root, include_weak):
|
|
"""musl sources to be built."""
|
|
sources = []
|
|
for d in os.listdir(os.path.join(musl_root, 'src')):
|
|
if d in DIR_BLACKLIST:
|
|
continue
|
|
base = os.path.join(musl_root, 'src', d)
|
|
pattern = os.path.join(base, '*.c')
|
|
for f in glob.glob(pattern):
|
|
if os.path.basename(f) in BLACKLIST:
|
|
continue
|
|
if not include_weak and os.path.basename(f) in WEAK_BLACKLIST:
|
|
continue
|
|
sources.append(f)
|
|
return sorted(sources)
|
|
|
|
|
|
def includes(musl, arch):
|
|
"""Include path."""
|
|
includes = [
|
|
os.path.join(musl, 'arch', arch),
|
|
os.path.join(musl, 'src', 'internal'),
|
|
os.path.join(musl, 'include')]
|
|
return list(itertools.chain(*zip(['-I'] * len(includes), includes)))
|
|
|
|
|
|
class Compiler(object):
|
|
"""Compile source files."""
|
|
def __init__(self, out, clang_dir, musl, arch, tmpdir):
|
|
self.out = out
|
|
self.outbase = os.path.basename(self.out)
|
|
self.clang_dir = clang_dir
|
|
self.musl = musl
|
|
self.arch = arch
|
|
self.tmpdir = tmpdir
|
|
self.compiled = []
|
|
|
|
def compile(self, sources):
|
|
if verbose:
|
|
self.compiled = sorted([self(source) for source in sources])
|
|
else:
|
|
pool = multiprocessing.Pool()
|
|
self.compiled = sorted(pool.map(self, sources))
|
|
pool.close()
|
|
pool.join()
|
|
|
|
|
|
class ObjCompiler(Compiler):
|
|
def __init__(self, out, clang_dir, musl, arch, tmpdir):
|
|
super(ObjCompiler, self).__init__(out, clang_dir, musl, arch, tmpdir)
|
|
|
|
def __call__(self, src):
|
|
target = 'wasm32-unknown-unknown-wasm'
|
|
compile_cmd = [os.path.join(self.clang_dir, 'clang'), '-target', target,
|
|
'-Os', '-c', '-nostdinc']
|
|
compile_cmd += includes(self.musl, self.arch)
|
|
compile_cmd += CFLAGS
|
|
check_output(compile_cmd + [src], cwd=self.tmpdir)
|
|
return os.path.basename(src)[:-1] + 'o' # .c -> .o
|
|
|
|
def binary(self):
|
|
if os.path.exists(self.out):
|
|
os.remove(self.out)
|
|
check_output([os.path.join(self.clang_dir, 'llvm-ar'), 'rcs', self.out] + self.compiled,
|
|
cwd=self.tmpdir)
|
|
|
|
|
|
class AsmCompiler(Compiler):
|
|
def __init__(self, out, clang_dir, musl, arch, tmpdir, binaryen_dir,
|
|
sexpr_wasm):
|
|
super(AsmCompiler, self).__init__(out, clang_dir, musl, arch, tmpdir)
|
|
self.binaryen_dir = binaryen_dir
|
|
self.sexpr_wasm = sexpr_wasm
|
|
|
|
def __call__(self, src):
|
|
target = 'wasm32-unknown-unknown'
|
|
compile_cmd = [os.path.join(self.clang_dir, 'clang'), '-target', target,
|
|
'-Os', '-emit-llvm', '-S', '-nostdinc']
|
|
compile_cmd += includes(self.musl, self.arch)
|
|
compile_cmd += CFLAGS
|
|
check_output(compile_cmd + [src], cwd=self.tmpdir)
|
|
return os.path.basename(src)[:-1] + 'll' # .c -> .ll
|
|
|
|
def binary(self):
|
|
max_memory = str(16 * 1024 * 1024)
|
|
bytecode = change_extension(self.out, '.bc')
|
|
assembly = os.path.join(self.tmpdir, self.outbase + '.s')
|
|
check_output([os.path.join(self.clang_dir, 'llvm-link'),
|
|
'-o', bytecode] + self.compiled,
|
|
cwd=self.tmpdir)
|
|
check_output([os.path.join(self.clang_dir, 'llc'),
|
|
bytecode, '-o', assembly],
|
|
cwd=self.tmpdir)
|
|
check_output([os.path.join(self.binaryen_dir, 's2wasm'),
|
|
assembly, '--ignore-unknown', '-o', self.out,
|
|
'--import-memory', '-m', max_memory],
|
|
cwd=self.tmpdir)
|
|
|
|
if self.sexpr_wasm:
|
|
check_output([self.sexpr_wasm,
|
|
self.out, '-o', change_extension(self.out, '.wasm')],
|
|
cwd=self.tmpdir)
|
|
|
|
|
|
def run(clang_dir, binaryen_dir, sexpr_wasm, musl, arch, out, save_temps,
|
|
compile_to_wasm):
|
|
if save_temps:
|
|
tmpdir = os.path.join(os.getcwd(), 'libc_build')
|
|
if os.path.isdir(tmpdir):
|
|
shutil.rmtree(tmpdir)
|
|
os.mkdir(tmpdir)
|
|
else:
|
|
tmpdir = tempfile.mkdtemp()
|
|
|
|
try:
|
|
create_version(musl)
|
|
build_alltypes(musl, arch)
|
|
sources = musl_sources(musl, include_weak=compile_to_wasm)
|
|
if compile_to_wasm:
|
|
compiler = ObjCompiler(out, clang_dir, musl, arch, tmpdir)
|
|
else:
|
|
compiler = AsmCompiler(out, clang_dir, musl, arch, tmpdir, binaryen_dir,
|
|
sexpr_wasm)
|
|
compiler.compile(sources)
|
|
compiler.binary()
|
|
if compile_to_wasm:
|
|
compiler.compile([os.path.join(musl, 'crt', 'crt1.c')])
|
|
shutil.copy(os.path.join(tmpdir, compiler.compiled[0]),
|
|
os.path.dirname(out))
|
|
finally:
|
|
if not save_temps:
|
|
shutil.rmtree(tmpdir)
|
|
|
|
|
|
def getargs():
|
|
parser = argparse.ArgumentParser(description='Build a hacky wasm libc.')
|
|
parser.add_argument('--clang_dir', type=str, required=True,
|
|
help='Clang binary directory')
|
|
parser.add_argument('--binaryen_dir', type=str, required=True,
|
|
help='binaryen binary directory')
|
|
parser.add_argument('--sexpr_wasm', type=str, required=False,
|
|
help='sexpr-wasm binary')
|
|
parser.add_argument('--musl', type=str, required=True,
|
|
help='musl libc root directory')
|
|
parser.add_argument('--arch', type=str, default='wasm32',
|
|
help='architecture to target')
|
|
parser.add_argument('--out', '-o', type=str,
|
|
default=os.path.join(os.getcwd(), 'musl.wast'),
|
|
help='Output file')
|
|
parser.add_argument('--save-temps', default=False, action='store_true',
|
|
help='Save temporary files')
|
|
parser.add_argument('--verbose', default=False, action='store_true',
|
|
help='Verbose')
|
|
parser.add_argument('--compile-to-wasm', default=False, action='store_true',
|
|
help='Use clang to compile directly to wasm')
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
global verbose
|
|
args = getargs()
|
|
if args.verbose:
|
|
verbose = True
|
|
return run(args.clang_dir, args.binaryen_dir, args.sexpr_wasm,
|
|
args.musl, args.arch, args.out, args.save_temps,
|
|
args.compile_to_wasm)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|