Skip to content

Commit c07738d

Browse files
committed
extra/gen_rodata_ld: Add gen_rodata_ld tool.
Analyzes an elf and extracts, sorts and writes out its rodata sections: - .rodata: sections WITH relocations → copied to RAM (LLEXT_MEM_RODATA) - .rodata_noreloc: sections WITHOUT relocations → kept in flash (LLEXT_MEM_RODATA_NO_RELOC) Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
1 parent 5f0e907 commit c07738d

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

extra/gen_rodata_ld.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package main
2+
3+
import (
4+
"debug/elf"
5+
"flag"
6+
"fmt"
7+
"os"
8+
"sort"
9+
"strings"
10+
)
11+
12+
func main() {
13+
flag.Usage = func() {
14+
fmt.Fprintf(os.Stderr, "Usage: %s <input.elf> [output_script.ld] [link_mode]\n\n", os.Args[0])
15+
fmt.Fprintf(os.Stderr, "Analyzes an ELF file and generates a linker script fragment that\n")
16+
fmt.Fprintf(os.Stderr, "separates .rodata sections into:\n")
17+
fmt.Fprintf(os.Stderr, " .rodata - sections WITH relocations (copied to RAM)\n")
18+
fmt.Fprintf(os.Stderr, " .rodata.noreloc - sections WITHOUT relocations (kept in flash)\n")
19+
fmt.Fprintf(os.Stderr, "\nIf link_mode is 'static', generates an empty linker script.\n")
20+
}
21+
22+
flag.Parse()
23+
if flag.NArg() < 1 {
24+
flag.Usage()
25+
os.Exit(1)
26+
}
27+
28+
inputFile := flag.Arg(0)
29+
outputFile := "rodata_split.ld"
30+
if flag.NArg() >= 2 {
31+
outputFile = flag.Arg(1)
32+
}
33+
linkMode := "dynamic"
34+
if flag.NArg() >= 3 {
35+
linkMode = flag.Arg(2)
36+
}
37+
38+
fmt.Printf("Generate rodata linker script (mode: %s)\n", linkMode)
39+
40+
// For static linking, generate empty linker script
41+
if linkMode == "static" {
42+
out, err := os.Create(outputFile)
43+
if err != nil {
44+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
45+
os.Exit(1)
46+
}
47+
defer out.Close()
48+
49+
fmt.Fprintf(out, "/* Empty linker script for static linking mode */\n")
50+
fmt.Printf("Generated: %s\n", outputFile)
51+
return
52+
}
53+
54+
f, err := elf.Open(inputFile)
55+
if err != nil {
56+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
57+
os.Exit(1)
58+
}
59+
defer f.Close()
60+
61+
// First pass: find all .rodata sections, indexed by section number
62+
type RodataSection struct {
63+
Name string
64+
HasRelocs bool
65+
}
66+
67+
rodataSections := make(map[uint32]*RodataSection)
68+
69+
for i, section := range f.Sections {
70+
if strings.HasPrefix(section.Name, ".rodata") {
71+
rodataSections[uint32(i)] = &RodataSection{Name: section.Name}
72+
}
73+
}
74+
75+
// Second pass: mark which rodata sections have relocations
76+
for _, section := range f.Sections {
77+
if section.Type == elf.SHT_REL || section.Type == elf.SHT_RELA {
78+
if rodata, exists := rodataSections[section.Info]; exists {
79+
rodata.HasRelocs = true
80+
}
81+
}
82+
}
83+
84+
// Separate and sort
85+
withRelocs := []string{}
86+
withoutRelocs := []string{}
87+
88+
for _, rodata := range rodataSections {
89+
if rodata.HasRelocs {
90+
withRelocs = append(withRelocs, rodata.Name)
91+
} else {
92+
withoutRelocs = append(withoutRelocs, rodata.Name)
93+
}
94+
}
95+
96+
sort.Strings(withRelocs)
97+
sort.Strings(withoutRelocs)
98+
99+
// Display
100+
fmt.Printf("rodata with relocations\n")
101+
for _, name := range withRelocs {
102+
fmt.Printf(" -%s\n", name)
103+
}
104+
fmt.Printf("\nrodata without relocations:\n")
105+
for _, name := range withoutRelocs {
106+
fmt.Printf(" -%s\n", name)
107+
}
108+
109+
// Generate linker script
110+
out, err := os.Create(outputFile)
111+
if err != nil {
112+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
113+
os.Exit(1)
114+
}
115+
defer out.Close()
116+
117+
// Helper function to print a section
118+
printSection := func(out *os.File, sectionName string, sections []string) {
119+
fmt.Fprintf(out, " %s : {\n", sectionName)
120+
for _, name := range sections {
121+
fmt.Fprintf(out, " *(%s)\n", name)
122+
}
123+
fmt.Fprintf(out, " }\n\n")
124+
}
125+
126+
fmt.Fprintf(out, "/* Auto-generated linker script fragment for LLEXT\n")
127+
fmt.Fprintf(out, " * Separates .rodata sections based on relocation status\n")
128+
fmt.Fprintf(out, " */\n\n")
129+
fmt.Fprintf(out, "SECTIONS\n{\n")
130+
131+
fmt.Fprintf(out, " /* Read-only data WITH relocations - will be copied to RAM by LLEXT */\n")
132+
printSection(out, ".rodata", withRelocs)
133+
134+
fmt.Fprintf(out, " /* Read-only data WITHOUT relocations - kept in flash by LLEXT */\n")
135+
printSection(out, ".rodata.noreloc", withoutRelocs)
136+
137+
fmt.Fprintf(out, " /* Merge all .rel.rodata.* sections into .rel.rodata */\n")
138+
printSection(out, ".rel.rodata", []string{".rel.rodata", ".rel.rodata.*"})
139+
140+
fmt.Fprintf(out, " /* Merge all .rela.rodata.* sections into .rela.rodata */\n")
141+
printSection(out, ".rela.rodata", []string{".rela.rodata", ".rela.rodata.*"})
142+
143+
fmt.Fprintf(out, "}\n")
144+
145+
fmt.Printf("Generated: %s\n", outputFile)
146+
}

0 commit comments

Comments
 (0)