@@ -897,6 +897,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
897897 // linkInputs contains .a archives from all packages and .o files from main module
898898 var linkInputs []string
899899 var linkArgs []string
900+ var publicAPISymbols []string
900901 var rtLinkInputs []string
901902 var rtLinkArgs []string
902903 linkedPkgs := make (map [string ]bool ) // Track linked packages by ID to avoid duplicates
@@ -929,6 +930,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
929930 if aPkg .LPkg .NeedAbiInit {
930931 needAbiInit = true
931932 }
933+ publicAPISymbols = append (publicAPISymbols , exportedSymbols (aPkg )... )
932934
933935 linkArgs = append (linkArgs , aPkg .LinkArgs ... )
934936 if aPkg .ArchiveFile != "" {
@@ -976,6 +978,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
976978 }
977979 }
978980 }
981+ linkArgs = append (linkArgs , ltoPublicAPIArgs (ctx , publicAPISymbols )... )
979982
980983 err = linkObjFiles (ctx , outputPath , linkInputs , linkArgs , verbose )
981984 if err != nil {
@@ -985,6 +988,68 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
985988 return nil
986989}
987990
991+ func exportedSymbols (aPkg * aPackage ) []string {
992+ if aPkg == nil || aPkg .LPkg == nil {
993+ return nil
994+ }
995+ exports := aPkg .LPkg .ExportFuncs ()
996+ if len (exports ) == 0 {
997+ return nil
998+ }
999+ symbols := make ([]string , 0 , len (exports ))
1000+ for _ , symbol := range exports {
1001+ symbols = append (symbols , symbol )
1002+ }
1003+ return symbols
1004+ }
1005+
1006+ func ltoPublicAPIArgs (ctx * context , symbols []string ) []string {
1007+ if ! isLTOEnabled (ctx .crossCompile ) {
1008+ return nil
1009+ }
1010+ symbols = normalizeSymbols (symbols )
1011+ if len (symbols ) == 0 {
1012+ return nil
1013+ }
1014+ return []string {
1015+ "-mllvm" ,
1016+ "-internalize-public-api-list=" + strings .Join (symbols , "," ),
1017+ }
1018+ }
1019+
1020+ func isLTOEnabled (cfg crosscompile.Export ) bool {
1021+ return hasLTOFlag (cfg .CFLAGS ) || hasLTOFlag (cfg .CCFLAGS ) || hasLTOFlag (cfg .LDFLAGS )
1022+ }
1023+
1024+ func hasLTOFlag (flags []string ) bool {
1025+ for _ , flag := range flags {
1026+ if strings .Contains (flag , "lto" ) {
1027+ return true
1028+ }
1029+ }
1030+ return false
1031+ }
1032+
1033+ func normalizeSymbols (symbols []string ) []string {
1034+ if len (symbols ) == 0 {
1035+ return nil
1036+ }
1037+ seen := make (map [string ]struct {}, len (symbols ))
1038+ out := make ([]string , 0 , len (symbols ))
1039+ for _ , symbol := range symbols {
1040+ if symbol == "" {
1041+ continue
1042+ }
1043+ if _ , ok := seen [symbol ]; ok {
1044+ continue
1045+ }
1046+ seen [symbol ] = struct {}{}
1047+ out = append (out , symbol )
1048+ }
1049+ slices .Sort (out )
1050+ return out
1051+ }
1052+
9881053// isRuntimePkg reports whether the package path belongs to the llgo runtime tree.
9891054func isRuntimePkg (pkgPath string ) bool {
9901055 rtRoot := env .LLGoRuntimePkg
0 commit comments