llvm_12.py
ofrak_patch_maker.toolchain.llvm_12
LLVM_12_0_1_Toolchain (Toolchain)
name: str
property
readonly
Returns:
Type | Description |
---|---|
str |
name property that matches the value used in |
_linker_script_flag: str
private
property
readonly
Returns:
Type | Description |
---|---|
str |
the linker script flag for this toolchain, usually |
segment_alignment: int
property
readonly
For example, x86 returns 16. This will most often be used when programmatically allocating memory for code/data.
Returns:
Type | Description |
---|---|
int |
required alignment factor for the toolchain/ISA |
_get_assembler_target(self, processor)
private
Red Balloon Security strongly recommends all users provide their specific hardware target for best results.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
processor |
ArchInfo |
required |
Returns:
Type | Description |
---|---|
str |
a default assembler target for the provided processor unless one is provided in |
Exceptions:
Type | Description |
---|---|
PatchMakerException |
if no target provided and program attributes do not correspond to a default value. |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def _get_assembler_target(self, processor: ArchInfo) -> str:
arch = processor.isa.value
if self._config.assembler_target:
return self._config.assembler_target
elif arch == InstructionSet.ARM.value:
return "armv7-a"
elif arch == InstructionSet.X86.value:
return "generic64"
else:
raise ToolchainException("Assembler Target not provided and no valid default found!")
_get_compiler_target(self, processor)
private
Returns a default compiler target for the provided processor unless one is provided
in self._config
.
Red Balloon Security strongly recommends all users provide their specific hardware target for best results.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
processor |
ArchInfo |
required |
Returns:
Type | Description |
---|---|
Optional[str] |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def _get_compiler_target(self, processor: ArchInfo) -> Optional[str]:
arch = processor.isa.value
if self._config.compiler_target:
return self._config.compiler_target
if arch == InstructionSet.ARM.value:
return "armv7---elf"
elif arch == InstructionSet.X86.value:
return "amd64---elf"
else:
raise ToolchainException("Compiler Target not provided and no valid default found!")
compile(self, c_file, header_dirs, out_dir='.')
Runs the Toolchain's C compiler on the input file.
Returns:
Type | Description |
---|---|
str |
path to the object file |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def compile(self, c_file: str, header_dirs: List[str], out_dir: str = ".") -> str:
if self.is_userspace():
out_file = os.path.join(out_dir, os.path.split(c_file)[-1] + ".o")
# For now a complete override of the flags; we sidestep the clang front-end
# in favor of a more userspace-friendly GNU configuration.
self._execute_tool(
self._compiler_path,
["-O3", "-Wall", "-g", "-fPIE", "-c"],
[c_file] + ["-I" + x for x in header_dirs],
out_file=out_file,
)
return os.path.abspath(out_file)
else:
return super().compile(c_file, header_dirs, out_dir=out_dir)
link(self, o_files, exec_path, script=None)
Run's the Toolchain
's linker on the input object files.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
o_files |
List[str] |
list of object files to be linked |
required |
exec_path |
str |
path to executable output file |
required |
script |
path to linker script (usually an |
None |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def link(self, o_files: List[str], exec_path: str, script=None):
if self.is_userspace():
# We will ignore the script and any lld flags in this case
flags = [
f"--dynamic-linker={self._config.userspace_dynamic_linker}",
f"-L{self._lib_path}",
]
return self._execute_tool(self._linker_path, flags, o_files, out_file=exec_path)
else:
return super().link(o_files, exec_path, script=script)
_get_linker_map_flag(exec_path)
private
staticmethod
Generates the linker map file flag for a linker invocation given the executable path.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
exec_path |
str |
path to executable |
required |
Returns:
Type | Description |
---|---|
path to map file |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
@staticmethod
def _get_linker_map_flag(exec_path: str):
return (f"--Map={exec_path}.map",)
add_linker_include_values(self, symbols, path)
Adds linker include entries to a provided file (usually ending in .inc
).
For example GNU syntax prescribes PROVIDE(name = 0xdeadbeef);
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
symbols |
Mapping[str, int] |
mapping of symbol string to effective address |
required |
path |
str |
path to the provided linker include file. |
required |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def add_linker_include_values(self, symbols: Mapping[str, int], path: str):
with open(path, "a") as f:
for name, addr in symbols.items():
if self.linker_include_filter(name):
continue
f.write(f"PROVIDE({name} = {hex(addr)});\n")
generate_linker_include_file(self, symbols, out_path)
This utility function receives the generated symbols dictionary that results
from preprocessing a firmware image and generates a .inc
file for use
with linker scripts, enabling direct function calls when using the complete
cross compilation toolchain.
This functionality must be defined for each toolchain given potential syntactical differences.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
symbols |
Mapping[str, int] |
mappings of symbol string to effective address |
required |
out_path |
str |
the path to the resulting symbol include file (usually |
required |
Returns:
Type | Description |
---|---|
str |
returns out_path |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def generate_linker_include_file(self, symbols: Mapping[str, int], out_path: str) -> str:
with open(out_path, "w") as f:
f.write(RBS_AUTOGEN_WARNING)
self.add_linker_include_values(symbols, out_path)
return out_path
ld_generate_region(self, object_path, segment_name, permissions, vm_address, length)
Generates regions for linker scripts.
Returns:
Type | Description |
---|---|
Tuple[str, str] |
a string entry for a "memory region" for the toolchain in question. |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def ld_generate_region(
self,
object_path: str,
segment_name: str,
permissions: MemoryPermissions,
vm_address: int,
length: int,
) -> Tuple[str, str]:
perms_string = self._ld_perm2str(permissions)
stripped_seg_name = segment_name.strip(".")
stripped_obj_name = os.path.basename(object_path).split(".")[0]
region_name = f'".rbs_{stripped_obj_name}_{stripped_seg_name}_mem"'
return (
f" {region_name} ({perms_string}) : ORIGIN = {hex(vm_address)}, LENGTH = {hex(length)}",
region_name,
)
ld_generate_bss_region(vm_address, length)
staticmethod
Generates .bss
regions for linker scripts.
Returns:
Type | Description |
---|---|
Tuple[str, str] |
a |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
@staticmethod
def ld_generate_bss_region(
vm_address: int,
length: int,
) -> Tuple[str, str]:
region_name = ".bss_mem"
perms_string = LLVM_12_0_1_Toolchain._ld_perm2str(MemoryPermissions.RW)
return (
f" {region_name} ({perms_string}) : ORIGIN = {hex(vm_address)}, LENGTH = {hex(length)}",
region_name,
)
ld_generate_section(object_path, segment_name, memory_region_name)
staticmethod
Generates sections for linker scripts.
Returns:
Type | Description |
---|---|
str |
a string entry for a "section" for the toolchain in question. |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
@staticmethod
def ld_generate_section(
object_path: str,
segment_name: str,
memory_region_name: str,
) -> str:
stripped_seg_name = segment_name.strip(".")
stripped_obj_name = os.path.basename(object_path).split(".")[0]
abs_path = os.path.abspath(object_path)
return (
f" .rbs_{stripped_obj_name}_{stripped_seg_name} : {{\n"
f" {abs_path}({segment_name}*)\n"
f" }} > {memory_region_name}"
)
ld_generate_bss_section(memory_region_name)
staticmethod
Generates .bss
sections for linker scripts.
Returns:
Type | Description |
---|---|
str |
a |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
@staticmethod
def ld_generate_bss_section(
memory_region_name: str,
) -> str:
bss_section_name = ".bss"
return (
f" {bss_section_name} : {{\n"
f" *.o({bss_section_name})\n"
f" }} > {memory_region_name}"
)
ld_script_create(self, name, memory_regions, sections, build_dir, symbol_files)
Constructs the linker script for the concrete toolchain class in use.
Uses the provided name, memory region strings, section strings, symbol files,
expected entrypoint (if any) to generate a linker script that results in a valid
FEM object when used within link
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name |
str |
required | |
memory_regions |
List[str] |
required | |
sections |
List[str] |
required | |
build_dir |
str |
required | |
symbol_files |
List[str] |
required |
Returns:
Type | Description |
---|---|
str |
path to the generated linker script |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def ld_script_create(
self,
name: str,
memory_regions: List[str],
sections: List[str],
build_dir: str,
symbol_files: List[str],
) -> str:
_, ld_script_path = tempfile.mkstemp(dir=build_dir, prefix=name + "_", suffix=".ld")
with open(ld_script_path, "w") as f:
f.write(RBS_AUTOGEN_WARNING)
for file in symbol_files:
f.write(f"INCLUDE {str(os.path.abspath(file))}\n")
f.write("\n\n")
f.write("MEMORY\n{\n")
for r in memory_regions:
f.write(r + "\n")
f.write("}\n")
f.write("\n")
f.write("SECTIONS\n{\n")
for s in sections:
f.write(s + "\n")
f.write("\n")
f.write(" /DISCARD/ : {\n")
for d in self._linker_discard_list:
f.write(f" *({d})\n")
f.write(" }\n")
f.write("}\n")
return ld_script_path
get_bin_file_symbols(self, executable_path)
For now, this utility only searches for global function and data symbols which are actually contained in a section in the file, as opposed to symbols which are referenced but undefined.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
executable_path |
str |
path to the program to be analyzed for symbols |
required |
Returns:
Type | Description |
---|---|
Dict[str, Tuple[int, ofrak_type.symbol_type.LinkableSymbolType]] |
mapping of symbol string to tuple of effective address, symbol type. |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def get_bin_file_symbols(
self, executable_path: str
) -> Dict[str, Tuple[int, LinkableSymbolType]]:
readobj_output = self._execute_tool(
self._readobj_path, ["--symbols"], [executable_path], out_file=None
)
return self._parser.parse_symbols(readobj_output)
get_bin_file_segments(self, path)
Returns:
Type | Description |
---|---|
Tuple[ofrak_patch_maker.toolchain.model.Segment, ...] |
list of segments |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def get_bin_file_segments(self, path: str) -> Tuple[Segment, ...]:
"""
:return: list of segments
"""
if get_file_format(path) != self.file_format:
raise ToolchainException(
"Extracted file format does not match this toolchain instance!"
)
readobj_output = self._execute_tool(
self._readobj_path, ["--section-details"], [path], out_file=None
)
return self._parser.parse_sections(readobj_output)
get_bin_file_rel_symbols(self, executable_path)
This utility searches for global function and data symbols which are referenced in a section in the file but are undefined.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
executable_path |
str |
path to the program to be analyzed for symbols |
required |
Returns:
Type | Description |
---|---|
Dict[str, Tuple[int, ofrak_type.symbol_type.LinkableSymbolType]] |
mapping of symbol string to tuple of effective address, symbol type. |
Source code in ofrak_patch_maker/toolchain/llvm_12.py
def get_bin_file_rel_symbols(
self, executable_path: str
) -> Dict[str, Tuple[int, LinkableSymbolType]]:
readobj_output = self._execute_tool(
self._readobj_path, ["--symbols"], [executable_path], out_file=None
)
return self._parser.parse_relocations(readobj_output)