blob: 17c3129876237f6846f9726a31d2a5d8c9e93bb3 [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"os/exec"
"strings"
)
var (
pythonExe = flag.String("python-exe", "REQUIRED", "Path to the Python interpreter executable.")
llvmReadelfExe = flag.String("llvm-readelf-exe", "REQUIRED", "Path to the llvm-readelf executable.")
outputFile = flag.String("output", "REQUIRED", "Path to the output file where PROVIDE statements will be written.")
)
func mustExecutable(exePath string, errorMsg string) string {
if exePath == "REQUIRED" {
log.Fatalf("Error: %s", errorMsg)
}
if _, err := os.Stat(exePath); err != nil {
if os.IsNotExist(err) {
log.Fatalf("Error: %s not found at '%s'", errorMsg, exePath)
} else {
log.Fatalf("Error: Failed to stat '%s': %v", exePath, err)
}
}
return exePath
}
type ReadelfJson []struct {
DynamicSymbols []struct {
Symbol struct {
Name struct {
Name string
}
Binding struct {
Name string
}
}
}
}
func main() {
flag.Parse()
pythonExe := mustExecutable(*pythonExe, "--python-exe is required")
llvmReadelfExe := mustExecutable(*llvmReadelfExe, "--llvm-readelf-exe is required")
if *outputFile == "REQUIRED" {
log.Fatal("Error: --output is required")
}
outputFile := *outputFile
cmd := exec.Command(llvmReadelfExe, "--dyn-syms", "--elf-output-style=JSON", pythonExe)
cmd.Stderr = os.Stderr
readelfOut, err := cmd.Output()
if err != nil {
log.Fatalf("Error running llvm-readelf: %v", err)
}
var readelfOutJson ReadelfJson
err = json.Unmarshal(readelfOut, &readelfOutJson)
if err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}
// Write PROVIDE statements to the output file by transforming each Python C API symbol into
// a corresponding linker script function to provide an equivalent weak symbol.
//
// The JSON from llvm-readelf looks like the following:
//
// [
// {
// ...,
// "DynamicSymbols": [
// {
// "Symbol": {
// "Name": {
// "Name": "PyCapsule_Type",
// ...
// },
// "Binding": {
// "Name": "Global",
// ...
// },
// ...
// }
// },
// ...
// ]
// },
// ...
// ]
var output []string
for _, entry := range readelfOutJson {
for _, symbol := range entry.DynamicSymbols {
if symbol.Symbol.Binding.Name == "Global" &&
(strings.HasPrefix(symbol.Symbol.Name.Name, "_Py") ||
strings.HasPrefix(symbol.Symbol.Name.Name, "Py")) {
output = append(output, fmt.Sprintf("PROVIDE(%s = 0);", symbol.Symbol.Name.Name))
}
}
}
err = os.WriteFile(outputFile, []byte(strings.Join(output, "\n")), 0644)
if err != nil {
log.Fatalf("Error writing to output file '%s': %v", outputFile, err)
}
}