From 4639ee13e7f7fa59315de0fe1e472c1b1afd67ad Mon Sep 17 00:00:00 2001 From: Haolan Date: Fri, 29 Aug 2025 11:30:25 +0800 Subject: [PATCH] feat: support compiler-rt --- internal/build/build.go | 2 +- internal/crosscompile/compile/compile.go | 99 +++++++++ .../{ => compile/libc}/newlibesp.go | 13 +- .../{ => compile/libc}/picolibc.go | 10 +- .../crosscompile/compile/rtlib/compiler_rt.go | 195 ++++++++++++++++++ internal/crosscompile/crosscompile.go | 39 +++- internal/crosscompile/fetch.go | 7 +- internal/crosscompile/libc.go | 117 ++--------- internal/targets/config.go | 1 + internal/targets/loader.go | 3 + targets/esp32.rom.newlib-funcs.ld | 130 ++++++++++++ 11 files changed, 497 insertions(+), 119 deletions(-) create mode 100644 internal/crosscompile/compile/compile.go rename internal/crosscompile/{ => compile/libc}/newlibesp.go (99%) rename internal/crosscompile/{ => compile/libc}/picolibc.go (97%) create mode 100644 internal/crosscompile/compile/rtlib/compiler_rt.go create mode 100644 targets/esp32.rom.newlib-funcs.ld diff --git a/internal/build/build.go b/internal/build/build.go index b13fac77..8892e084 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -907,7 +907,7 @@ define weak void @_start() { if !needStart(ctx.buildConf) && isWasmTarget(ctx.buildConf.Goos) { mainDefine = "define hidden noundef i32 @__main_argc_argv(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr" } - if !needStart(ctx.buildConf) { + if true { startDefine = "" } mainCode := fmt.Sprintf(`; ModuleID = 'main' diff --git a/internal/crosscompile/compile/compile.go b/internal/crosscompile/compile/compile.go new file mode 100644 index 00000000..5165b39f --- /dev/null +++ b/internal/crosscompile/compile/compile.go @@ -0,0 +1,99 @@ +package compile + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "slices" + "strings" + + "github.com/goplus/llgo/internal/clang" +) + +type CompileGroup struct { + OutputFileName string + Files []string // List of source files to compile + CFlags []string // C compiler flags + CCFlags []string + LDFlags []string // Linker flags +} + +func (g CompileGroup) IsCompiled(outputDir string) bool { + archive := filepath.Join(outputDir, g.OutputFileName) + _, err := os.Stat(archive) + return !os.IsNotExist(err) +} + +func (g CompileGroup) Compile(outputDir, cc, linkerName string, extraCCFlags, extraLDFlags []string) (err error) { + if g.IsCompiled(outputDir) { + return + } + tmpCompileDir, err := os.MkdirTemp("", "compile-group*") + if err != nil { + return + } + defer os.RemoveAll(tmpCompileDir) + + compileLDFlags := append(slices.Clone(extraLDFlags), g.LDFlags...) + compileCCFlags := append(slices.Clone(extraCCFlags), g.CCFlags...) + cfg := clang.NewConfig(cc, compileCCFlags, g.CFlags, compileLDFlags, linkerName) + + var objFiles []string + + compiler := clang.NewCompiler(cfg) + + compiler.Verbose = true + + archive := filepath.Join(outputDir, g.OutputFileName) + fmt.Fprintf(os.Stderr, "Start to compile group %s to %s...\n", g.OutputFileName, archive) + + for _, file := range g.Files { + var tempObjFile *os.File + tempObjFile, err = os.CreateTemp(tmpCompileDir, fmt.Sprintf("%s*.o", strings.ReplaceAll(file, string(os.PathSeparator), "-"))) + if err != nil { + return + } + fmt.Fprintf(os.Stderr, "Compile file %s to %s...\n", file, tempObjFile.Name()) + + lang := "c" + if filepath.Ext(file) == ".S" { + lang = "assembler-with-cpp" + } + err = compiler.Compile("-o", tempObjFile.Name(), "-x", lang, "-c", file) + if err != nil { + return + } + + objFiles = append(objFiles, tempObjFile.Name()) + } + + args := []string{"rcs", archive} + args = append(args, objFiles...) + + ccDir := filepath.Dir(cc) + llvmAr := filepath.Join(ccDir, "llvm-ar") + + cmd := exec.Command(llvmAr, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + return +} + +// CompileConfig represents compilation configuration +type CompileConfig struct { + Url string + Name string // compile name (e.g., "picolibc", "musl", "glibc") + Groups []CompileGroup + ArchiveSrcDir string +} + +func (c CompileConfig) IsCompiled(outputDir string) bool { + for _, group := range c.Groups { + if !group.IsCompiled(outputDir) { + return false + } + } + return true +} diff --git a/internal/crosscompile/newlibesp.go b/internal/crosscompile/compile/libc/newlibesp.go similarity index 99% rename from internal/crosscompile/newlibesp.go rename to internal/crosscompile/compile/libc/newlibesp.go index d70723f1..7dc99059 100644 --- a/internal/crosscompile/newlibesp.go +++ b/internal/crosscompile/compile/libc/newlibesp.go @@ -1,20 +1,22 @@ -package crosscompile +package libc import ( "path/filepath" + + "github.com/goplus/llgo/internal/crosscompile/compile" ) // getNewlibESP32Config returns configuration for newlib esp32 -func getNewlibESP32Config(baseDir, arch string) *compileLibcConfig { +func GetNewlibESP32Config(baseDir, arch string) *compile.CompileConfig { libcDir := filepath.Join(baseDir, "newlib", "libc") // headerFile, _ := os.Create(filepath.Join(baseDir, "picolibc.h")) // headerFile.Close() - return &compileLibcConfig{ + return &compile.CompileConfig{ Url: "https://github.com/MeteorsLiu/newlib-esp32/archive/refs/heads/esp-4.3.0.zip", Name: "newlib-esp32", - Groups: []compileGroup{ + Groups: []compile.CompileGroup{ { OutputFileName: "libcrt0.a", Files: []string{ @@ -23,7 +25,6 @@ func getNewlibESP32Config(baseDir, arch string) *compileLibcConfig { filepath.Join(baseDir, "libgloss", "xtensa", "boards", "esp32", "board.c"), filepath.Join(baseDir, "libgloss", "xtensa", "crt1-boards.S"), filepath.Join(baseDir, "libgloss", "xtensa", "sleep.S"), - filepath.Join(baseDir, "libgloss", "xtensa", "window-vectors.S"), }, CFlags: []string{ "-DHAVE_CONFIG_H", @@ -974,7 +975,7 @@ func getNewlibESP32Config(baseDir, arch string) *compileLibcConfig { "-I" + filepath.Join(libcDir, "posix"), "-I" + filepath.Join(libcDir, "stdlib"), }, - LDFlags: []string{"-nostdlib", "-L" + baseDir, "-lgloss"}, + LDFlags: []string{"-nostdlib"}, CCFlags: []string{ "-Oz", "-fno-builtin", diff --git a/internal/crosscompile/picolibc.go b/internal/crosscompile/compile/libc/picolibc.go similarity index 97% rename from internal/crosscompile/picolibc.go rename to internal/crosscompile/compile/libc/picolibc.go index b31405d6..319fbad2 100644 --- a/internal/crosscompile/picolibc.go +++ b/internal/crosscompile/compile/libc/picolibc.go @@ -1,12 +1,14 @@ -package crosscompile +package libc import ( "os" "path/filepath" + + "github.com/goplus/llgo/internal/crosscompile/compile" ) // getPicolibcConfig returns configuration for picolibc -func getPicolibcConfig(baseDir string) *compileLibcConfig { +func GetPicolibcConfig(baseDir string) *compile.CompileConfig { libcIncludeDir := filepath.Join(baseDir, "libc", "include") libmIncludeDir := filepath.Join(baseDir, "libm", "common") localeIncludeDir := filepath.Join(baseDir, "libc", "locale") @@ -16,10 +18,10 @@ func getPicolibcConfig(baseDir string) *compileLibcConfig { headerFile, _ := os.Create(filepath.Join(baseDir, "picolibc.h")) headerFile.Close() - return &compileLibcConfig{ + return &compile.CompileConfig{ Url: "https://github.com/picolibc/picolibc/releases/download/1.8.10/picolibc-1.8.10.tar.xz", Name: "picolibc", - Groups: []compileGroup{ + Groups: []compile.CompileGroup{ { OutputFileName: "libc.a", Files: []string{ diff --git a/internal/crosscompile/compile/rtlib/compiler_rt.go b/internal/crosscompile/compile/rtlib/compiler_rt.go new file mode 100644 index 00000000..5ffa0ba0 --- /dev/null +++ b/internal/crosscompile/compile/rtlib/compiler_rt.go @@ -0,0 +1,195 @@ +package rtlib + +import ( + "path/filepath" + + "github.com/goplus/llgo/internal/crosscompile/compile" +) + +func GetCompilerRTConfig(baseDir, arch string) *compile.CompileConfig { + return &compile.CompileConfig{ + Url: "https://github.com/MeteorsLiu/llvm-project/archive/refs/heads/compiler-rt.zip", + ArchiveSrcDir: "llvm-project-compiler-rt", + Groups: []compile.CompileGroup{ + { + OutputFileName: "libclang_builtins.a", + Files: []string{ + filepath.Join(baseDir, "lib", "builtins", "xtensa/ieee754_sqrtf.S"), + filepath.Join(baseDir, "lib", "builtins", "absvdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "absvsi2.c"), + filepath.Join(baseDir, "lib", "builtins", "absvti2.c"), + filepath.Join(baseDir, "lib", "builtins", "adddf3.c"), + filepath.Join(baseDir, "lib", "builtins", "addsf3.c"), + filepath.Join(baseDir, "lib", "builtins", "addvdi3.c"), + filepath.Join(baseDir, "lib", "builtins", "addvsi3.c"), + filepath.Join(baseDir, "lib", "builtins", "addvti3.c"), + filepath.Join(baseDir, "lib", "builtins", "apple_versioning.c"), + filepath.Join(baseDir, "lib", "builtins", "ashldi3.c"), + filepath.Join(baseDir, "lib", "builtins", "ashlti3.c"), + filepath.Join(baseDir, "lib", "builtins", "ashrdi3.c"), + filepath.Join(baseDir, "lib", "builtins", "ashrti3.c"), + filepath.Join(baseDir, "lib", "builtins", "bswapdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "bswapsi2.c"), + filepath.Join(baseDir, "lib", "builtins", "clzdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "clzsi2.c"), + filepath.Join(baseDir, "lib", "builtins", "clzti2.c"), + filepath.Join(baseDir, "lib", "builtins", "cmpdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "cmpti2.c"), + filepath.Join(baseDir, "lib", "builtins", "comparedf2.c"), + filepath.Join(baseDir, "lib", "builtins", "comparesf2.c"), + filepath.Join(baseDir, "lib", "builtins", "ctzdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "ctzsi2.c"), + filepath.Join(baseDir, "lib", "builtins", "ctzti2.c"), + filepath.Join(baseDir, "lib", "builtins", "divdc3.c"), + filepath.Join(baseDir, "lib", "builtins", "divdf3.c"), + filepath.Join(baseDir, "lib", "builtins", "divdi3.c"), + filepath.Join(baseDir, "lib", "builtins", "divmoddi4.c"), + filepath.Join(baseDir, "lib", "builtins", "divmodsi4.c"), + filepath.Join(baseDir, "lib", "builtins", "divmodti4.c"), + filepath.Join(baseDir, "lib", "builtins", "divsc3.c"), + filepath.Join(baseDir, "lib", "builtins", "divsf3.c"), + filepath.Join(baseDir, "lib", "builtins", "divsi3.c"), + filepath.Join(baseDir, "lib", "builtins", "divti3.c"), + filepath.Join(baseDir, "lib", "builtins", "extendsfdf2.c"), + filepath.Join(baseDir, "lib", "builtins", "extendhfsf2.c"), + filepath.Join(baseDir, "lib", "builtins", "ffsdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "ffssi2.c"), + filepath.Join(baseDir, "lib", "builtins", "ffsti2.c"), + filepath.Join(baseDir, "lib", "builtins", "fixdfdi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixdfsi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixdfti.c"), + filepath.Join(baseDir, "lib", "builtins", "fixsfdi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixsfsi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixsfti.c"), + filepath.Join(baseDir, "lib", "builtins", "fixunsdfdi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixunsdfsi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixunsdfti.c"), + filepath.Join(baseDir, "lib", "builtins", "fixunssfdi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixunssfsi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixunssfti.c"), + filepath.Join(baseDir, "lib", "builtins", "floatdidf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatdisf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatsidf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatsisf.c"), + filepath.Join(baseDir, "lib", "builtins", "floattidf.c"), + filepath.Join(baseDir, "lib", "builtins", "floattisf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatundidf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatundisf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatunsidf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatunsisf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatuntidf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatuntisf.c"), + filepath.Join(baseDir, "lib", "builtins", "fp_mode.c"), + filepath.Join(baseDir, "lib", "builtins", "int_util.c"), + filepath.Join(baseDir, "lib", "builtins", "lshrdi3.c"), + filepath.Join(baseDir, "lib", "builtins", "lshrti3.c"), + filepath.Join(baseDir, "lib", "builtins", "moddi3.c"), + filepath.Join(baseDir, "lib", "builtins", "modsi3.c"), + filepath.Join(baseDir, "lib", "builtins", "modti3.c"), + filepath.Join(baseDir, "lib", "builtins", "muldc3.c"), + filepath.Join(baseDir, "lib", "builtins", "muldf3.c"), + filepath.Join(baseDir, "lib", "builtins", "muldi3.c"), + filepath.Join(baseDir, "lib", "builtins", "mulodi4.c"), + filepath.Join(baseDir, "lib", "builtins", "mulosi4.c"), + filepath.Join(baseDir, "lib", "builtins", "muloti4.c"), + filepath.Join(baseDir, "lib", "builtins", "mulsc3.c"), + filepath.Join(baseDir, "lib", "builtins", "mulsf3.c"), + filepath.Join(baseDir, "lib", "builtins", "multi3.c"), + filepath.Join(baseDir, "lib", "builtins", "mulvdi3.c"), + filepath.Join(baseDir, "lib", "builtins", "mulvsi3.c"), + filepath.Join(baseDir, "lib", "builtins", "mulvti3.c"), + filepath.Join(baseDir, "lib", "builtins", "negdf2.c"), + filepath.Join(baseDir, "lib", "builtins", "negdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "negsf2.c"), + filepath.Join(baseDir, "lib", "builtins", "negti2.c"), + filepath.Join(baseDir, "lib", "builtins", "negvdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "negvsi2.c"), + filepath.Join(baseDir, "lib", "builtins", "negvti2.c"), + filepath.Join(baseDir, "lib", "builtins", "os_version_check.c"), + filepath.Join(baseDir, "lib", "builtins", "paritydi2.c"), + filepath.Join(baseDir, "lib", "builtins", "paritysi2.c"), + filepath.Join(baseDir, "lib", "builtins", "parityti2.c"), + filepath.Join(baseDir, "lib", "builtins", "popcountdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "popcountsi2.c"), + filepath.Join(baseDir, "lib", "builtins", "popcountti2.c"), + filepath.Join(baseDir, "lib", "builtins", "powidf2.c"), + filepath.Join(baseDir, "lib", "builtins", "powisf2.c"), + filepath.Join(baseDir, "lib", "builtins", "subdf3.c"), + filepath.Join(baseDir, "lib", "builtins", "subsf3.c"), + filepath.Join(baseDir, "lib", "builtins", "subvdi3.c"), + filepath.Join(baseDir, "lib", "builtins", "subvsi3.c"), + filepath.Join(baseDir, "lib", "builtins", "subvti3.c"), + filepath.Join(baseDir, "lib", "builtins", "trampoline_setup.c"), + filepath.Join(baseDir, "lib", "builtins", "truncdfhf2.c"), + filepath.Join(baseDir, "lib", "builtins", "truncdfsf2.c"), + filepath.Join(baseDir, "lib", "builtins", "truncsfhf2.c"), + filepath.Join(baseDir, "lib", "builtins", "ucmpdi2.c"), + filepath.Join(baseDir, "lib", "builtins", "ucmpti2.c"), + filepath.Join(baseDir, "lib", "builtins", "udivdi3.c"), + filepath.Join(baseDir, "lib", "builtins", "udivmoddi4.c"), + filepath.Join(baseDir, "lib", "builtins", "udivmodsi4.c"), + filepath.Join(baseDir, "lib", "builtins", "udivmodti4.c"), + filepath.Join(baseDir, "lib", "builtins", "udivsi3.c"), + filepath.Join(baseDir, "lib", "builtins", "udivti3.c"), + filepath.Join(baseDir, "lib", "builtins", "umoddi3.c"), + filepath.Join(baseDir, "lib", "builtins", "umodsi3.c"), + filepath.Join(baseDir, "lib", "builtins", "umodti3.c"), + filepath.Join(baseDir, "lib", "builtins", "gcc_personality_v0.c"), + filepath.Join(baseDir, "lib", "builtins", "clear_cache.c"), + filepath.Join(baseDir, "lib", "builtins", "addtf3.c"), + filepath.Join(baseDir, "lib", "builtins", "comparetf2.c"), + filepath.Join(baseDir, "lib", "builtins", "divtc3.c"), + filepath.Join(baseDir, "lib", "builtins", "divtf3.c"), + filepath.Join(baseDir, "lib", "builtins", "extenddftf2.c"), + filepath.Join(baseDir, "lib", "builtins", "extendhftf2.c"), + filepath.Join(baseDir, "lib", "builtins", "extendsftf2.c"), + filepath.Join(baseDir, "lib", "builtins", "fixtfdi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixtfsi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixtfti.c"), + filepath.Join(baseDir, "lib", "builtins", "fixunstfdi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixunstfsi.c"), + filepath.Join(baseDir, "lib", "builtins", "fixunstfti.c"), + filepath.Join(baseDir, "lib", "builtins", "floatditf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatsitf.c"), + filepath.Join(baseDir, "lib", "builtins", "floattitf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatunditf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatunsitf.c"), + filepath.Join(baseDir, "lib", "builtins", "floatuntitf.c"), + filepath.Join(baseDir, "lib", "builtins", "multc3.c"), + filepath.Join(baseDir, "lib", "builtins", "multf3.c"), + filepath.Join(baseDir, "lib", "builtins", "powitf2.c"), + filepath.Join(baseDir, "lib", "builtins", "subtf3.c"), + filepath.Join(baseDir, "lib", "builtins", "trunctfdf2.c"), + filepath.Join(baseDir, "lib", "builtins", "trunctfhf2.c"), + filepath.Join(baseDir, "lib", "builtins", "trunctfsf2.c"), + }, + CFlags: []string{ + "-DNDEBUG", + "-DVISIBILITY_HIDDEN", + }, + CCFlags: []string{ + "-Oz", + "-fno-ident", + "-Wno-unused-parameter", + "-fno-lto", + "-Werror=array-bounds", + "-Werror=uninitialized", + "-Werror=shadow", + "-Werror=empty-body", + "-Werror=sizeof-pointer-memaccess", + "-Werror=sizeof-array-argument", + "-Werror=suspicious-memaccess", + "-Werror=builtin-memcpy-chk-size", + "-Werror=array-bounds-pointer-arithmetic", + "-Werror=return-stack-address", + "-Werror=sizeof-array-decay", + "-Werror=format-insufficient-args", + "-Wformat -std=c11", + "-fno-builtin", + "-fvisibility=hidden", + "-fomit-frame-pointer", + }, + }, + }, + } +} diff --git a/internal/crosscompile/crosscompile.go b/internal/crosscompile/crosscompile.go index 79749534..f6e2664e 100644 --- a/internal/crosscompile/crosscompile.go +++ b/internal/crosscompile/crosscompile.go @@ -10,6 +10,7 @@ import ( "runtime" "strings" + "github.com/goplus/llgo/internal/crosscompile/compile" "github.com/goplus/llgo/internal/env" "github.com/goplus/llgo/internal/targets" "github.com/goplus/llgo/internal/xtool/llvm" @@ -219,15 +220,8 @@ func ldFlagsFromFileName(fileName string) string { return strings.TrimPrefix(strings.TrimSuffix(fileName, ".a"), "lib") } -func getOrCompileLibc(cc, linkerName, libcName string, exportCCFlags, exportLDFlags []string) (ldflags []string, err error) { - baseDir := filepath.Join(cacheRoot(), "crosscompile") - outputDir := filepath.Join(baseDir, libcName) - - compileConfig, err := getCompileLibcConfigByName(baseDir, libcName) - if err != nil { - return - } - if err = checkDownloadAndExtractLibc(compileConfig, compileConfig.Url, outputDir, compileConfig.ArchiveSrcDir); err != nil { +func getOrCompileWithConfig(compileConfig *compile.CompileConfig, outputDir, cc, linkerName, libName string, exportCCFlags, exportLDFlags []string) (ldflags []string, err error) { + if err = checkDownloadAndExtractLib(compileConfig, compileConfig.Url, outputDir, compileConfig.ArchiveSrcDir); err != nil { return } ldflags = append(ldflags, "-nostdlib", "-L"+outputDir) @@ -606,13 +600,38 @@ func useTarget(targetName string) (export Export, err error) { if config.Libc != "" { var libcLDFlags []string - libcLDFlags, err = getOrCompileLibc(export.CC, export.Linker, config.Libc, ccflags, ldflags) + var compileConfig *compile.CompileConfig + baseDir := filepath.Join(cacheRoot(), "crosscompile") + outputDir := filepath.Join(baseDir, config.Libc) + + compileConfig, err = getLibcCompileConfigByName(baseDir, config.Libc) + if err != nil { + return + } + libcLDFlags, err = getOrCompileWithConfig(compileConfig, outputDir, export.CC, export.Linker, config.Libc, ccflags, ldflags) if err != nil { return } ldflags = append(ldflags, libcLDFlags...) } + if config.RTLib != "" { + var rtLibLDFlags []string + var compileConfig *compile.CompileConfig + baseDir := filepath.Join(cacheRoot(), "crosscompile") + outputDir := filepath.Join(baseDir, config.RTLib) + + compileConfig, err = getRTCompileConfigByName(baseDir, config.RTLib) + if err != nil { + return + } + rtLibLDFlags, err = getOrCompileWithConfig(compileConfig, outputDir, export.CC, export.Linker, config.RTLib, ccflags, ldflags) + if err != nil { + return + } + ldflags = append(ldflags, rtLibLDFlags...) + } + // Combine with config flags and expand template variables export.CFLAGS = cflags export.CCFLAGS = ccflags diff --git a/internal/crosscompile/fetch.go b/internal/crosscompile/fetch.go index cc676662..950ca19b 100644 --- a/internal/crosscompile/fetch.go +++ b/internal/crosscompile/fetch.go @@ -13,6 +13,8 @@ import ( "path/filepath" "strings" "syscall" + + "github.com/goplus/llgo/internal/crosscompile/compile" ) // checkDownloadAndExtractWasiSDK downloads and extracts WASI SDK @@ -80,7 +82,7 @@ func checkDownloadAndExtractESPClang(platformSuffix, dir string) error { return nil } -func checkDownloadAndExtractLibc(cfg *compileLibcConfig, url, dstDir, internalArchiveSrcDir string) error { +func checkDownloadAndExtractLib(cfg *compile.CompileConfig, url, dstDir, internalArchiveSrcDir string) error { // Check if already exists if cfg.IsCompiled(dstDir) { return nil @@ -107,7 +109,7 @@ func checkDownloadAndExtractLibc(cfg *compileLibcConfig, url, dstDir, internalAr if err := downloadAndExtractArchive(url, tempExtractDir, description); err != nil { return err } - // defer os.RemoveAll(tempExtractDir) + defer os.RemoveAll(tempExtractDir) srcDir := tempExtractDir @@ -115,6 +117,7 @@ func checkDownloadAndExtractLibc(cfg *compileLibcConfig, url, dstDir, internalAr srcDir = filepath.Join(tempExtractDir, internalArchiveSrcDir) } + os.RemoveAll(dstDir) if err := os.Rename(srcDir, dstDir); err != nil { return fmt.Errorf("failed to rename libc directory: %w", err) } diff --git a/internal/crosscompile/libc.go b/internal/crosscompile/libc.go index d9fe64bc..8bd96e85 100644 --- a/internal/crosscompile/libc.go +++ b/internal/crosscompile/libc.go @@ -2,105 +2,16 @@ package crosscompile import ( "fmt" - "os" - "os/exec" "path/filepath" - "slices" - "strings" - "github.com/goplus/llgo/internal/clang" + "github.com/goplus/llgo/internal/crosscompile/compile" + "github.com/goplus/llgo/internal/crosscompile/compile/libc" + "github.com/goplus/llgo/internal/crosscompile/compile/rtlib" ) -type compileGroup struct { - OutputFileName string - Files []string // List of source files to compile - CFlags []string // C compiler flags specific to this libc - CCFlags []string - LDFlags []string // Linker flags -} - -func (g compileGroup) IsCompiled(outputDir string) bool { - libcArchive := filepath.Join(outputDir, g.OutputFileName) - _, err := os.Stat(libcArchive) - return !os.IsNotExist(err) -} - -func (g compileGroup) Compile(outputDir, cc, linkerName string, extraCCFlags, extraLDFlags []string) (err error) { - if g.IsCompiled(outputDir) { - return - } - tmpCompileDir, err := os.MkdirTemp("", "compile-libc-group*") - if err != nil { - return - } - defer os.RemoveAll(tmpCompileDir) - - compileLDFlags := append(slices.Clone(extraLDFlags), g.LDFlags...) - compileCCFlags := append(slices.Clone(extraCCFlags), g.CCFlags...) - cfg := clang.NewConfig(cc, compileCCFlags, g.CFlags, compileLDFlags, linkerName) - - var objFiles []string - - compiler := clang.NewCompiler(cfg) - - compiler.Verbose = true - - libcArchive := filepath.Join(outputDir, g.OutputFileName) - fmt.Fprintf(os.Stderr, "Start to compile libc group %s to %s...\n", g.OutputFileName, libcArchive) - - for _, file := range g.Files { - var tempObjFile *os.File - tempObjFile, err = os.CreateTemp(tmpCompileDir, fmt.Sprintf("%s*.o", strings.ReplaceAll(file, string(os.PathSeparator), "-"))) - if err != nil { - return - } - fmt.Fprintf(os.Stderr, "Compile libc file %s to %s...\n", file, tempObjFile.Name()) - - lang := "c" - if filepath.Ext(file) == ".S" { - lang = "assembler-with-cpp" - } - err = compiler.Compile("-o", tempObjFile.Name(), "-x", lang, "-c", file) - if err != nil { - return - } - - objFiles = append(objFiles, tempObjFile.Name()) - } - - args := []string{"rcs", libcArchive} - args = append(args, objFiles...) - - ccDir := filepath.Dir(cc) - llvmAr := filepath.Join(ccDir, "llvm-ar") - - cmd := exec.Command(llvmAr, args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Run() - return -} - -// CompileLibcConfig represents libc compilation configuration -type compileLibcConfig struct { - Url string - Name string // Libc name (e.g., "picolibc", "musl", "glibc") - Groups []compileGroup - ArchiveSrcDir string -} - -func (c compileLibcConfig) IsCompiled(outputDir string) bool { - for _, group := range c.Groups { - if !group.IsCompiled(outputDir) { - return false - } - } - return true -} - -// GetCompileLibcConfigByName retrieves libc compilation configuration by name +// GetCompileConfigByName retrieves libc compilation configuration by name // Returns compilation file lists and corresponding cflags -func getCompileLibcConfigByName(baseDir, libcName string) (*compileLibcConfig, error) { +func getLibcCompileConfigByName(baseDir, libcName string) (*compile.CompileConfig, error) { if libcName == "" { return nil, fmt.Errorf("libc name cannot be empty") } @@ -108,10 +19,24 @@ func getCompileLibcConfigByName(baseDir, libcName string) (*compileLibcConfig, e switch libcName { case "picolibc": - return getPicolibcConfig(libcDir), nil + return libc.GetPicolibcConfig(libcDir), nil case "newlib-esp32": - return getNewlibESP32Config(libcDir, "xtensa"), nil + return libc.GetNewlibESP32Config(libcDir, "xtensa"), nil default: return nil, fmt.Errorf("unsupported libc: %s", libcName) } } + +func getRTCompileConfigByName(baseDir, rtName string) (*compile.CompileConfig, error) { + if rtName == "" { + return nil, fmt.Errorf("rt name cannot be empty") + } + rtDir := filepath.Join(baseDir, rtName) + + switch rtName { + case "compiler-rt": + return rtlib.GetCompilerRTConfig(rtDir, "xtensa"), nil + default: + return nil, fmt.Errorf("unsupported rt: %s", rtName) + } +} diff --git a/internal/targets/config.go b/internal/targets/config.go index 4071e9fe..d75d1f45 100644 --- a/internal/targets/config.go +++ b/internal/targets/config.go @@ -17,6 +17,7 @@ type Config struct { // Compiler and linker configuration Libc string `json:"libc"` + RTLib string `json:"rtlib"` Linker string `json:"linker"` LinkerScript string `json:"linkerscript"` CFlags []string `json:"cflags"` diff --git a/internal/targets/loader.go b/internal/targets/loader.go index 15687ccd..95d1e73a 100644 --- a/internal/targets/loader.go +++ b/internal/targets/loader.go @@ -137,6 +137,9 @@ func (l *Loader) mergeConfig(dst, src *Config) { if src.Libc != "" { dst.Libc = src.Libc } + if src.RTLib != "" { + dst.RTLib = src.RTLib + } if src.Linker != "" { dst.Linker = src.Linker } diff --git a/targets/esp32.rom.newlib-funcs.ld b/targets/esp32.rom.newlib-funcs.ld new file mode 100644 index 00000000..79d5c5be --- /dev/null +++ b/targets/esp32.rom.newlib-funcs.ld @@ -0,0 +1,130 @@ +/* These are the newlib functions present in ESP32 ROM. + They should not be used when compiling with PSRAM cache workaround enabled. + See also esp32.rom.newlib-data.ld for the list of .data/.bss symbols + used by these functions, and esp32.rom.newlib-nano.ld for "nano" versions + of printf/scanf family of functions. + + Unlike other ROM functions which are exported using PROVIDE, which declares + weak symbols, newlib related functions are exported using assignment, + which declares strong symbols. This is done so that ROM functions are always + used instead of the ones provided by libc.a. + + Time functions were moved to the esp32.rom.newlib-time.ld file. + */ + +abs = 0x40056340; +__ascii_wctomb = 0x40058ef0; +atoi = 0x400566c4; +_atoi_r = 0x400566d4; +atol = 0x400566ec; +_atol_r = 0x400566fc; +bzero = 0x4000c1f4; +_cleanup = 0x40001df8; +_cleanup_r = 0x40001d48; +creat = 0x40000e8c; +div = 0x40056348; +__dummy_lock = 0x4000c728; +__dummy_lock_try = 0x4000c730; +__env_lock = 0x40001fd4; +__env_unlock = 0x40001fe0; +fclose = 0x400020ac; +_fclose_r = 0x40001fec; +fflush = 0x40059394; +_fflush_r = 0x40059320; +_findenv_r = 0x40001f44; +__fp_lock_all = 0x40001f1c; +__fp_unlock_all = 0x40001f30; +__fputwc = 0x40058da0; +fputwc = 0x40058ea8; +_fputwc_r = 0x40058e4c; +_fwalk = 0x4000c738; +_fwalk_reent = 0x4000c770; +_getenv_r = 0x40001fbc; +isalnum = 0x40000f04; +isalpha = 0x40000f18; +isascii = 0x4000c20c; +isblank = 0x40000f2c; +iscntrl = 0x40000f50; +isdigit = 0x40000f64; +isgraph = 0x40000f94; +islower = 0x40000f78; +isprint = 0x40000fa8; +ispunct = 0x40000fc0; +isspace = 0x40000fd4; +isupper = 0x40000fe8; +__itoa = 0x40056678; +itoa = 0x400566b4; +labs = 0x40056370; +ldiv = 0x40056378; +longjmp = 0x400562cc; +memccpy = 0x4000c220; +memchr = 0x4000c244; +memcmp = 0x4000c260; +memcpy = 0x4000c2c8; +memmove = 0x4000c3c0; +memrchr = 0x4000c400; +memset = 0x4000c44c; +qsort = 0x40056424; +rand = 0x40001058; +rand_r = 0x400010d4; +__sccl = 0x4000c498; +__sclose = 0x400011b8; +__seofread = 0x40001148; +setjmp = 0x40056268; +__sflush_r = 0x400591e0; +__sfmoreglue = 0x40001dc8; +__sfp = 0x40001e90; +__sfp_lock_acquire = 0x40001e08; +__sfp_lock_release = 0x40001e14; +__sinit = 0x40001e38; +__sinit_lock_acquire = 0x40001e20; +__sinit_lock_release = 0x40001e2c; +srand = 0x40001004; +__sread = 0x40001118; +__sseek = 0x40001184; +strcasecmp = 0x400011cc; +strcasestr = 0x40001210; +strcat = 0x4000c518; +strchr = 0x4000c53c; +strcmp = 0x40001274; +strcoll = 0x40001398; +strcpy = 0x400013ac; +strcspn = 0x4000c558; +strdup = 0x4000143c; +_strdup_r = 0x40001450; +strlcat = 0x40001470; +strlcpy = 0x4000c584; +strlen = 0x400014c0; +strlwr = 0x40001524; +strncasecmp = 0x40001550; +strncat = 0x4000c5c4; +strncmp = 0x4000c5f4; +strncpy = 0x400015d4; +strndup = 0x400016b0; +_strndup_r = 0x400016c4; +strnlen = 0x4000c628; +strrchr = 0x40001708; +strsep = 0x40001734; +strspn = 0x4000c648; +strstr = 0x4000c674; +__strtok_r = 0x4000c6a8; +strtok_r = 0x4000c70c; +strtol = 0x4005681c; +_strtol_r = 0x40056714; +strtoul = 0x4005692c; +_strtoul_r = 0x40056834; +strupr = 0x4000174c; +__submore = 0x40058f3c; +__swbuf = 0x40058cb4; +__swbuf_r = 0x40058bec; +__swrite = 0x40001150; +toascii = 0x4000c720; +tolower = 0x40001868; +toupper = 0x40001884; +ungetc = 0x400590f4; +_ungetc_r = 0x40058fa0; +__utoa = 0x400561f0; +utoa = 0x40056258; +wcrtomb = 0x40058920; +_wcrtomb_r = 0x400588d8; +_wctomb_r = 0x40058f14;