33#include < cassert>
44#include < cstdint>
55#include < cstring>
6+ #include < iostream>
67#include < map>
78#include < stdexcept>
89#include < sys/mman.h>
@@ -24,6 +25,91 @@ struct LoadedModule {
2425std::vector<LoadedModule> loaded_modules;
2526std::unordered_set<std::string> loaded_module_names;
2627
28+ // Flag: true if any SO has PC32 dyn_relocs (requires all SOs in low address space)
29+ bool need_low_address = false ;
30+ std::unordered_set<std::string> scanned_names;
31+
32+ // Helper to load FLE from file (searches FLE_LIBRARY_PATH)
33+ FLEObject load_fle_with_path (const std::string& filename)
34+ {
35+ // Try direct path
36+ try {
37+ return load_fle (filename);
38+ } catch (...) {
39+ }
40+
41+ try {
42+ return load_fle (filename + " .fle" );
43+ } catch (...) {
44+ }
45+
46+ // Search in FLE_LIBRARY_PATH
47+ const char * lib_path_env = std::getenv (" FLE_LIBRARY_PATH" );
48+ if (lib_path_env != nullptr ) {
49+ std::string lib_path (lib_path_env);
50+ std::string basename = filename;
51+ size_t last_slash = filename.rfind (' /' );
52+ if (last_slash != std::string::npos) {
53+ basename = filename.substr (last_slash + 1 );
54+ }
55+
56+ std::vector<std::string> paths;
57+ size_t start = 0 ;
58+ size_t end = lib_path.find (' :' );
59+ while (end != std::string::npos) {
60+ if (end > start)
61+ paths.push_back (lib_path.substr (start, end - start));
62+ start = end + 1 ;
63+ end = lib_path.find (' :' , start);
64+ }
65+ if (start < lib_path.size ())
66+ paths.push_back (lib_path.substr (start));
67+
68+ for (const auto & path : paths) {
69+ try {
70+ return load_fle (path + " /" + basename);
71+ } catch (...) {
72+ }
73+ try {
74+ return load_fle (path + " /" + filename);
75+ } catch (...) {
76+ }
77+ }
78+ }
79+ throw std::runtime_error (" Could not load: " + filename);
80+ }
81+
82+ // Pre-scan dependencies to check if any SO has PC32 dyn_relocs
83+ void scan_dependencies_recursive (const std::string& filename)
84+ {
85+ if (scanned_names.count (filename))
86+ return ;
87+
88+ FLEObject obj;
89+ try {
90+ obj = load_fle_with_path (filename);
91+ } catch (...) {
92+ return ; // Will fail later during actual load
93+ }
94+
95+ scanned_names.insert (filename);
96+
97+ // Check for PC32 dyn_relocs
98+ if (obj.type == " .so" ) {
99+ for (const auto & reloc : obj.dyn_relocs ) {
100+ if (reloc.type == RelocationType::R_X86_64_PC32) {
101+ need_low_address = true ;
102+ break ;
103+ }
104+ }
105+ }
106+
107+ // Recurse into dependencies
108+ for (const auto & dep : obj.needed ) {
109+ scan_dependencies_recursive (dep);
110+ }
111+ }
112+
27113// Helper to resolve a symbol across all loaded modules
28114uint64_t resolve_symbol (const std::string& name)
29115{
@@ -48,18 +134,76 @@ void load_module_recursive(const std::string& filename)
48134 }
49135
50136 // Load FLE file
51- // Handle potential .fle extension issues if filenames in 'needed' lack it
137+ // Try direct path first, then search in FLE_LIBRARY_PATH
52138 FLEObject obj;
139+ bool loaded = false ;
140+
141+ // Try direct path
53142 try {
54143 obj = load_fle (filename);
144+ loaded = true ;
55145 } catch (...) {
146+ // Try with extensions
56147 try {
57148 obj = load_fle (filename + " .fle" );
149+ loaded = true ;
58150 } catch (...) {
59- throw std::runtime_error (" Could not load dependency: " + filename);
151+ // Continue to search in library path
152+ }
153+ }
154+
155+ // If not found, search in FLE_LIBRARY_PATH
156+ if (!loaded) {
157+ const char * lib_path_env = std::getenv (" FLE_LIBRARY_PATH" );
158+ if (lib_path_env != nullptr ) {
159+ std::string lib_path (lib_path_env);
160+ std::string basename = filename;
161+ // Extract basename from path if it contains directory separators
162+ size_t last_slash = filename.rfind (' /' );
163+ if (last_slash != std::string::npos) {
164+ basename = filename.substr (last_slash + 1 );
165+ }
166+
167+ // Split FLE_LIBRARY_PATH by ':'
168+ std::vector<std::string> paths;
169+ size_t start = 0 ;
170+ size_t end = lib_path.find (' :' );
171+ while (end != std::string::npos) {
172+ if (end > start) {
173+ paths.push_back (lib_path.substr (start, end - start));
174+ }
175+ start = end + 1 ;
176+ end = lib_path.find (' :' , start);
177+ }
178+ if (start < lib_path.size ()) {
179+ paths.push_back (lib_path.substr (start));
180+ }
181+
182+ // Try each path
183+ for (const auto & path : paths) {
184+ std::string full_path = path + " /" + basename;
185+ try {
186+ obj = load_fle (full_path);
187+ loaded = true ;
188+ break ;
189+ } catch (...) {
190+ // Try original filename (maybe it's a relative path)
191+ try {
192+ obj = load_fle (path + " /" + filename);
193+ loaded = true ;
194+ break ;
195+ } catch (...) {
196+ // Continue searching
197+ }
198+ }
199+ }
60200 }
61201 }
62202
203+ if (!loaded) {
204+ throw std::runtime_error (" Could not load dependency: " + filename);
205+ }
206+
63207 loaded_module_names.insert (filename);
64208
65209 // Prepare LoadedModule structure
@@ -90,8 +234,21 @@ void load_module_recursive(const std::string& filename)
90234
91235 if (has_segments) {
92236 uint64_t total_size = max_end;
93- // Reserve memory region
94- void * addr = mmap (NULL , total_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1 , 0 );
237+
238+ void * addr;
239+ if (need_low_address) {
240+ // Use MAP_32BIT for PC32 text relocations (can only reach ±2GB)
241+ std::cerr << " Warning: Loading " << filename << " into low 32-bit address space due to PC32 relocations." << std::endl;
242+ addr = mmap (NULL , total_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1 , 0 );
243+ if (addr == MAP_FAILED) {
244+ // Fallback without MAP_32BIT
245+ addr = mmap (NULL , total_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1 , 0 );
246+ }
247+ } else {
248+ // PIC code (GOT/PLT with R_X86_64_64) can be loaded anywhere
249+ addr = mmap (NULL , total_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1 , 0 );
250+ }
251+
95252 if (addr == MAP_FAILED) {
96253 throw std::runtime_error (" Failed to reserve memory for shared library" );
97254 }
@@ -155,6 +312,14 @@ void FLE_exec(const FLEObject& obj)
155312 // Clear globals for fresh execution
156313 loaded_modules.clear ();
157314 loaded_module_names.clear ();
315+ scanned_names.clear ();
316+ need_low_address = false ;
317+
318+ // Pre-scan all dependencies to check if any SO has PC32 dyn_relocs
319+ // This must be done BEFORE loading so we know whether to use MAP_32BIT
320+ for (const auto & dep : obj.needed ) {
321+ scan_dependencies_recursive (dep);
322+ }
158323
159324 // 1. Load Main Executable (Manual setup for the main object provided)
160325 // We treat the passed object as the first module but we need its name.
@@ -205,13 +370,21 @@ void FLE_exec(const FLEObject& obj)
205370 // 2. Perform Relocations for ALL modules
206371 for (auto & mod : loaded_modules) {
207372
208- // Helper lambda to apply a single relocation
209- auto apply_reloc = [&](const Relocation& reloc, uint64_t section_base_vaddr) {
210- uint64_t sym_addr = resolve_symbol (reloc.symbol );
211- uint64_t reloc_addr = mod.load_base + section_base_vaddr + reloc.offset ;
373+ // A. Dynamic Relocations (Bonus 1 - Text Relocations for SO, Bonus 2 - GOT for EXE)
374+ // For .so: dyn_relocs.offset is relative to merged section data (typically .text)
375+ // For .exe: dyn_relocs.offset is VMA (already resolved during linking)
376+ for (const auto & reloc : mod.obj .dyn_relocs ) {
377+ uint64_t reloc_addr;
378+
379+ if (mod.obj .type == " .exe" ) {
380+ // For executables, offset is the VMA
381+ reloc_addr = reloc.offset ;
382+ } else {
383+ // For shared objects, offset is VMA relative to Load Base
384+ reloc_addr = mod.load_base + reloc.offset ;
385+ }
212386
213- // Check if reloc_addr is valid memory?
214- // Ideally we should check, but assuming correctness of ELF/FLE
387+ uint64_t sym_addr = resolve_symbol (reloc.symbol );
215388
216389 switch (reloc.type ) {
217390 case RelocationType::R_X86_64_64:
@@ -227,19 +400,10 @@ void FLE_exec(const FLEObject& obj)
227400 // S + A - P
228401 *(uint32_t *)reloc_addr = (uint32_t )(sym_addr + reloc.addend - reloc_addr);
229402 break ;
403+ case RelocationType::R_X86_64_GOTPCREL:
404+ *(uint32_t *)reloc_addr = (uint32_t )(sym_addr + reloc.addend - reloc_addr);
405+ break ;
230406 }
231- };
232-
233- // A. Dynamic Relocations (Bonus 2 - GOT)
234- // These are typically absolute relocations in the GOT section
235- // dyn_relocs have offset relative to load_base?
236- // In FLE, headers are segments. dyn_relocs usually point to GOT which is in a segment.
237- // Assuming reloc.offset is VMA.
238- for (const auto & reloc : mod.obj .dyn_relocs ) {
239- // For dynamic relocs, offset is usually VMA.
240- // So address is load_base + offset.
241- // pass 0 as section_base since offset includes it.
242- apply_reloc (reloc, 0 );
243407 }
244408
245409 // B. Section Relocations (Bonus 1 - Text Relocations)
@@ -287,6 +451,9 @@ void FLE_exec(const FLEObject& obj)
287451 case RelocationType::R_X86_64_PC32:
288452 *(uint32_t *)reloc_addr = (uint32_t )(sym_addr + reloc.addend - reloc_addr);
289453 break ;
454+ case RelocationType::R_X86_64_GOTPCREL:
455+ *(uint32_t *)reloc_addr = (uint32_t )(sym_addr + reloc.addend - reloc_addr);
456+ break ;
290457 }
291458 }
292459 }
0 commit comments