Skip to content

Commit 5da72e2

Browse files
committed
fix: support for PIC in dynamic linking
1 parent b137d8b commit 5da72e2

File tree

4 files changed

+865
-572
lines changed

4 files changed

+865
-572
lines changed

include/fle.hpp

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -234,20 +234,6 @@ class FLEWriter {
234234
result["needed"] = needed;
235235
}
236236

237-
void write_dynamic_relocs(const std::vector<Relocation>& relocs)
238-
{
239-
json relocs_json = json::array();
240-
for (const auto& reloc : relocs) {
241-
json reloc_json;
242-
reloc_json["type"] = reloc.type;
243-
reloc_json["offset"] = reloc.offset;
244-
reloc_json["symbol"] = reloc.symbol;
245-
reloc_json["addend"] = reloc.addend;
246-
relocs_json.push_back(reloc_json);
247-
}
248-
result["dyn_relocs"] = relocs_json;
249-
}
250-
251237
private:
252238
std::string current_section;
253239
json result;
@@ -324,4 +310,4 @@ void FLE_readfle(const FLEObject& obj);
324310
*/
325311
void FLE_disasm(const FLEObject& obj, const std::string& section_name);
326312

327-
#endif
313+
#endif

src/base/exec.cpp

Lines changed: 189 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
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 {
2425
std::vector<LoadedModule> loaded_modules;
2526
std::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
28114
uint64_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

Comments
 (0)