mirror of
https://github.com/linux-msm/xbltools.git
synced 2024-11-23 09:53:33 +08:00
initial commit
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
This commit is contained in:
commit
dd62298001
67
.clang-format
Normal file
67
.clang-format
Normal file
@ -0,0 +1,67 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# clang-format configuration file. Intended for clang-format >= 11.
|
||||
#
|
||||
# For more information, see:
|
||||
#
|
||||
# Documentation/process/clang-format.rst
|
||||
# https://clang.llvm.org/docs/ClangFormat.html
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 100
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
IndentWidth: 8
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
UseTab: Always
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: false
|
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
_*
|
||||
*.elf
|
||||
*.dll
|
||||
*.mbn
|
||||
*.hash
|
||||
*.pbn
|
||||
*.hd
|
||||
build/
|
82
README.md
Normal file
82
README.md
Normal file
@ -0,0 +1,82 @@
|
||||
# XBL Tools
|
||||
|
||||
Tools for manipulating Qualcomm XBL images.
|
||||
|
||||
## unpackxbl
|
||||
|
||||
This tool uses brute-force and heuristics to split a Qualcomm XBL partition
|
||||
image into it's three respective parts.
|
||||
|
||||
1. sbl1.elf
|
||||
2. xbl_core.elf
|
||||
3. xbl_sec.mbn
|
||||
|
||||
## The components
|
||||
|
||||
### sbl1
|
||||
|
||||
sbl1 is the pre-loader responsible for loading everything else, it does some
|
||||
basic hw init, DDR training, and loads tz, hyp, abl, and other images from
|
||||
storage. Then it jumps to xbl_core via the hypervisor.
|
||||
|
||||
### xbl_core
|
||||
|
||||
This is the actual EDKII based UEFI bootloader, when people refer to Qualcomm
|
||||
"XBL" they usually just mean this part. It receives some data from sbl1 which
|
||||
includes the address of where ABL was loaded. ABL is just a single .efi
|
||||
executable (`LinuxLoader.efi` which contains the actual Android boot image
|
||||
handling and fastboot implementation). It's wrapped in an EFI Firmware Volume
|
||||
(FV) with an ELF header appended. On Android/IoT configurations XBL will always
|
||||
launch `LinuxLoader.efi`.
|
||||
|
||||
### xbl_sec
|
||||
|
||||
This binary is validated separetely to the rest of XBL, it is also the first
|
||||
code to run. It initialises EL3 before jumping to the pre-loader. The entrypoint
|
||||
of xbl.elf is actually for xbl_sec.
|
||||
|
||||
## Why?
|
||||
|
||||
By being able to unpack and existing XBL image, we can then re-pack it again
|
||||
using the
|
||||
[createxbl.py](https://github.com/coreboot/coreboot/blob/main/util/qualcomm/createxbl.py)
|
||||
tool which is readily available in the coreboot repo.
|
||||
|
||||
Now, leveraging what we know about the structure of these ELF files, we can
|
||||
provide a drop-in replacement for xbl_core, namely: U-Boot. We can build U-Boot
|
||||
with the correct TEXT_BASE, have it generate an ELF file and then build a new
|
||||
XBL image which will boot directly into U-Boot instead of into EDKII.
|
||||
|
||||
## On my device?
|
||||
|
||||
Unfortunately, re-generating the XBL image necessarily means re-generating the
|
||||
hash and re-signing it. If your device is unfused (doesn't have Qualcomm
|
||||
secureboot enabled), and signing images using
|
||||
[qtestsign](https://github.com/msm8916-mainline/qtestsign) works for you, then
|
||||
you can give this approach a try. Outside of development boards and engineering
|
||||
samples, the only devices using XBL without secureboot that I'm aware of are the
|
||||
SHIFT6mq and TCL Book 14 Go.
|
||||
|
||||
## Building
|
||||
|
||||
```sh
|
||||
meson setup build
|
||||
meson compile -C build
|
||||
```
|
||||
|
||||
## Using
|
||||
|
||||
```sh
|
||||
./build/unpackxbl /path/to/stock/xbl.elf
|
||||
```
|
||||
|
||||
One can then build a new XBL image with (for example) U-Boot as the payload by
|
||||
using `createxbl.py` from coreboot. This is known to work for SDM845, but other
|
||||
SoCs may have some issues with the MBN header and certs as generated by
|
||||
coreboot's `createxbl.py`. Running with `--no_hash` and using a different tool
|
||||
to generate the header is recommended.
|
||||
|
||||
```sh
|
||||
../coreboot/utils/qualcomm/createxbl.py -f ./sbl1.elf -s u-boot.elf -x ./xbl_sec.mbn -a 64 -b 64 -d 64 --mbn_version=5 -o uboot1st.elf
|
||||
fastboot flash xbl uboot1st.elf reboot
|
||||
```
|
17
meson.build
Normal file
17
meson.build
Normal file
@ -0,0 +1,17 @@
|
||||
project('xbltools', 'c')
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
deps = [
|
||||
]
|
||||
|
||||
inc = [
|
||||
]
|
||||
|
||||
src = [
|
||||
'unpackxbl.c',
|
||||
]
|
||||
|
||||
executable('unpackxbl', src,
|
||||
include_directories: inc,
|
||||
dependencies: deps,
|
||||
install: true)
|
391
unpackxbl.c
Normal file
391
unpackxbl.c
Normal file
@ -0,0 +1,391 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// clang-format off
|
||||
/*
|
||||
* Copyright 2023 Linaro Ltd.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*
|
||||
* This program extracts the sbl1.elf, XBL_Core.elf, and xbl_sec.mbn
|
||||
* binaries from a packed XBL image. The XBL_Core.elf binary has its own ELF header
|
||||
* merged into the XBL.elf image when producing the final xbl.mbn.
|
||||
*
|
||||
* Example XBL image layout (SDM845):
|
||||
*
|
||||
* Elf file type is EXEC (Executable file)
|
||||
* Entry point 0x148492b8
|
||||
* There are 16 program headers, starting at offset 64
|
||||
*
|
||||
* Program Headers:
|
||||
* Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
|
||||
* NULL 0x000000 0x0000000000000000 0x0000000000000000 0x0003c0 0x000000 0
|
||||
* NULL 0x001000 0x000000009fe00000 0x000000009fe00000 0x001b28 0x002000 0x1000
|
||||
* LOAD 0x003000 0x000000001483f000 0x000000001483f000 0x04d7e0 0x04d7e0 R E 0x1000
|
||||
* LOAD 0x0507e0 0x000000001488f000 0x000000001488f000 0x000000 0x003000 RW 0x1000
|
||||
* LOAD 0x0507e0 0x0000000014892000 0x0000000014892000 0x00cb86 0x00cb86 RW 0x1000
|
||||
* LOAD 0x05d370 0x000000001489f000 0x000000001489f000 0x000000 0x018c00 RW 0x1000
|
||||
* LOAD 0x05d370 0x0000000085e00000 0x0000000085e00000 0x000000 0x027ba0 RW 0x1000
|
||||
* LOAD 0x05d370 0x00000000146ae000 0x00000000146ae000 0x001380 0x001380 R E 0x1000
|
||||
* LOAD 0x05e6f0 0x00000000146b1000 0x00000000146b1000 0x000844 0x000844 RW 0x1000
|
||||
* LOAD 0x05ef40 0x00000000148bf000 0x00000000148bf000 0x0218c0 0x0218c0 RWE 0x1000 <-- XBLRamdump (probably??)
|
||||
* LOAD 0x080800 0x00000000146b2000 0x00000000146b2000 0x000000 0x002d34 RW 0x1000
|
||||
* LOAD 0x080800 0x000000009fc00000 0x000000009fc00000 0x200000 0x200000 RWE 0x1000 <-- xbl_core.elf (ELF header stripped)
|
||||
* LOAD 0x280800 0x0000000014699000 0x0000000014699000 0x013b25 0x013b25 R E 0x1000 <-- xbl_sec.mbn (includes ELF header)
|
||||
* LOAD 0x294330 0x0000000085e35000 0x0000000085e35000 0x042b1b 0x042b1b R E 0x1000
|
||||
* LOAD 0x2d6e50 0x0000000085ea7000 0x0000000085ea7000 0x04b800 0x04b804 RW 0x1000
|
||||
* LOAD 0x322650 0x0000000085e97000 0x0000000085e97000 0x000000 0x001dd0 RW 0x1000
|
||||
*/
|
||||
// clang-format on
|
||||
|
||||
#include <elf.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BIT(x) (1 << (x))
|
||||
|
||||
#define XBL_SEC_FLAGS (BIT(24) | BIT(26))
|
||||
|
||||
/*
|
||||
* On at least SDM845 and QRB4210, the lowest 28 bits of
|
||||
* the address are the same. So for now use this heuristic
|
||||
* to detect it.
|
||||
*/
|
||||
#define XBL_CORE_ADDR_MATCH 0xfc00000
|
||||
|
||||
static bool verbose = false;
|
||||
static void *sbl1 = NULL;
|
||||
static uint64_t sbl1_offset = 0;
|
||||
static size_t sbl1_size = 4096;
|
||||
// static Elf64_Ehdr xblldr_ehdr;
|
||||
|
||||
#define log(...) \
|
||||
do { \
|
||||
if (verbose) \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static int usage()
|
||||
{
|
||||
// clang-format off
|
||||
fprintf(stderr, "unpackxbl: extract sbl1.elf, XBL_Core.elf, and xbl_sec.mbn\n");
|
||||
fprintf(stderr, "-------------------------------------------\n");
|
||||
fprintf(stderr, "unpackxbl [-v] [-o OUTDIR] /path/to/xbl.elf\n");
|
||||
fprintf(stderr, " -v enable verbose logging\n");
|
||||
// clang-format on
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void init_header(void *buf, uint64_t addr)
|
||||
{
|
||||
Elf64_Ehdr ehdr = {
|
||||
.e_ident = {
|
||||
[EI_CLASS] = ELFCLASS64,
|
||||
[EI_DATA] = ELFDATA2LSB,
|
||||
[EI_VERSION] = EV_CURRENT,
|
||||
[EI_OSABI] = ELFOSABI_NONE,
|
||||
},
|
||||
.e_type = ET_EXEC,
|
||||
.e_machine = EM_AARCH64,
|
||||
.e_version = EV_CURRENT,
|
||||
.e_entry = addr,
|
||||
.e_phoff = sizeof(ehdr),
|
||||
.e_shoff = 0,
|
||||
.e_flags = 0,
|
||||
.e_ehsize = sizeof(ehdr),
|
||||
.e_phentsize = sizeof(Elf64_Phdr),
|
||||
.e_phnum = 1,
|
||||
.e_shentsize = 0,
|
||||
.e_shnum = 0,
|
||||
.e_shstrndx = 0,
|
||||
};
|
||||
|
||||
/* Copy in the ELF magic */
|
||||
memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
|
||||
|
||||
memcpy(buf, &ehdr, sizeof(ehdr));
|
||||
}
|
||||
|
||||
static void init_phdr(void *buf, uint64_t addr, size_t size)
|
||||
{
|
||||
Elf64_Phdr phdr = {
|
||||
.p_type = PT_LOAD,
|
||||
/* Yes we need the write attribute too */
|
||||
.p_flags = PF_R | PF_X | PF_W,
|
||||
.p_offset = sizeof(Elf64_Ehdr) + sizeof(phdr),
|
||||
.p_vaddr = addr,
|
||||
.p_paddr = addr,
|
||||
.p_filesz = size,
|
||||
.p_memsz = size,
|
||||
.p_align = 0x1000,
|
||||
};
|
||||
|
||||
memcpy(buf, &phdr, sizeof(phdr));
|
||||
}
|
||||
|
||||
/* Create a new ELF header with a single program header and write it out */
|
||||
static int write_part(FILE *out, uint64_t addr, void *buf, size_t size)
|
||||
{
|
||||
Elf64_Ehdr ehdr = { 0 };
|
||||
Elf64_Phdr phdr = { 0 };
|
||||
|
||||
init_header(&ehdr, addr);
|
||||
|
||||
init_phdr(&phdr, addr, size);
|
||||
|
||||
if (fwrite(&ehdr, sizeof(ehdr), 1, out) != 1) {
|
||||
fprintf(stderr, "Failed to write ELF header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite(&phdr, sizeof(phdr), 1, out) != 1) {
|
||||
fprintf(stderr, "Failed to write program header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite(buf, size, 1, out) != 1) {
|
||||
fprintf(stderr, "Failed to write %lu bytes\n", size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fflush(out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unpack_xbl(FILE *xbl)
|
||||
{
|
||||
Elf64_Ehdr ehdr;
|
||||
int n_phdrs = 0;
|
||||
Elf64_Phdr xbl_core_phdr = { 0 };
|
||||
Elf64_Phdr xbl_sec_phdr = { 0 };
|
||||
|
||||
if (fread(&ehdr, sizeof(ehdr), 1, xbl) != 1) {
|
||||
fprintf(stderr, "Failed to read ELF header\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
|
||||
fprintf(stderr, "Invalid ELF magic\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
fprintf(stderr, "Only 64-bit ELF supported\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) {
|
||||
fprintf(stderr, "Only little endian ELF supported\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initially allocate space for the header, we'll grow this on demand */
|
||||
sbl1 = malloc(sbl1_size);
|
||||
init_header(sbl1, ehdr.e_entry);
|
||||
sbl1_offset += sizeof(Elf64_Ehdr);
|
||||
|
||||
/* Iterate through the program headers, skip the hash segments, XBL_Core and XBLSec */
|
||||
for (int i = 0; i < ehdr.e_phnum; i++) {
|
||||
Elf64_Phdr phdr;
|
||||
|
||||
if (fseek(xbl, ehdr.e_phoff + (i * sizeof(phdr)), SEEK_SET) != 0) {
|
||||
fprintf(stderr, "Failed to seek to program header %d\n", i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fread(&phdr, sizeof(phdr), 1, xbl) != 1) {
|
||||
fprintf(stderr, "Failed to read program header %d\n", i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
log("%d: type=%d offset=%#8lx vaddr=%#18lx paddr=%#18lx filesz=%#8lx memsz=0x%lx flags=0x%x align=%#06lx\n",
|
||||
i, phdr.p_type, phdr.p_offset, phdr.p_vaddr, phdr.p_paddr, phdr.p_filesz,
|
||||
phdr.p_memsz, phdr.p_flags, phdr.p_align);
|
||||
|
||||
/* Skip non-loadable segments */
|
||||
if (phdr.p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
/* Skip XBL_Core and XBLSec */
|
||||
if ((phdr.p_paddr & 0xFFFFFFF) == XBL_CORE_ADDR_MATCH) {
|
||||
memcpy(&xbl_core_phdr, &phdr, sizeof(phdr));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((phdr.p_flags & XBL_SEC_FLAGS) == XBL_SEC_FLAGS) {
|
||||
memcpy(&xbl_sec_phdr, &phdr, sizeof(phdr));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Copy the program header to the sbl1 buffer */
|
||||
memcpy(sbl1 + sbl1_offset, &phdr, sizeof(phdr));
|
||||
sbl1_offset += sizeof(phdr);
|
||||
n_phdrs++;
|
||||
}
|
||||
|
||||
if (!xbl_core_phdr.p_paddr) {
|
||||
fprintf(stderr, "Failed to find XBL_Core program header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!xbl_sec_phdr.p_paddr) {
|
||||
fprintf(stderr, "Failed to find XBLSec program header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the number of program headers */
|
||||
((Elf64_Ehdr *)sbl1)->e_phnum = n_phdrs;
|
||||
|
||||
/* We have now figured out which program headers are for which of the three images. XBL_Core.elf
|
||||
* is always made up of a single image (one program header), same for xbl_sec.mbn. And all the rest
|
||||
* are what make up sbl1.elf.
|
||||
* So at this point, we walk through all the program headers which we know are for sbl1.elf
|
||||
* and write their associated data to the buffer.
|
||||
*/
|
||||
for (int i = 0; i < n_phdrs; i++) {
|
||||
Elf64_Phdr *phdr =
|
||||
(Elf64_Phdr *)(sbl1 + sizeof(Elf64_Ehdr) + (i * sizeof(Elf64_Phdr)));
|
||||
|
||||
if (fseek(xbl, phdr->p_offset, SEEK_SET) != 0) {
|
||||
fprintf(stderr, "Failed to seek to offset 0x%lx\n", phdr->p_offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Grow the buffer if needed */
|
||||
if (sbl1_offset + phdr->p_filesz > sbl1_size) {
|
||||
sbl1 = realloc(sbl1, sbl1_offset + phdr->p_filesz);
|
||||
if (!sbl1) {
|
||||
fprintf(stderr, "Failed to allocate %lu bytes\n",
|
||||
sbl1_offset + phdr->p_filesz);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FIXME: Don't forget to update the pointer :sob: */
|
||||
phdr = (Elf64_Phdr *)(sbl1 + sizeof(Elf64_Ehdr) +
|
||||
(i * sizeof(Elf64_Phdr)));
|
||||
}
|
||||
|
||||
/* Update p_offset to point to the right file offset */
|
||||
phdr->p_offset = sbl1_offset;
|
||||
|
||||
/* Some empty program headers are just used to reserve RAM, now that we've
|
||||
* updated the offset, we can skip them.
|
||||
*/
|
||||
if (!phdr->p_filesz)
|
||||
continue;
|
||||
|
||||
/* Read the data into the buffer */
|
||||
if (fread(sbl1 + sbl1_offset, phdr->p_filesz, 1, xbl) != 1) {
|
||||
fprintf(stderr, "Failed to read %lu bytes (phdr vaddr %#012lx)\n",
|
||||
phdr->p_filesz, phdr->p_vaddr);
|
||||
return -1;
|
||||
}
|
||||
sbl1_offset += phdr->p_filesz;
|
||||
}
|
||||
|
||||
/* Write out the sbl1 binary */
|
||||
FILE *out = fopen("sbl1.elf", "wb");
|
||||
if (!out) {
|
||||
fprintf(stderr, "Failed to open sbl1.elf for writing\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite(sbl1, sbl1_offset, 1, out) != 1) {
|
||||
fprintf(stderr, "Failed to write sbl1.elf\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fflush(out);
|
||||
fclose(out);
|
||||
|
||||
/* Write out the XBL_Core binary */
|
||||
out = fopen("xbl_core.elf", "wb");
|
||||
if (!out) {
|
||||
fprintf(stderr, "Failed to open XBL_Core.elf for writing\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *buf = malloc(xbl_core_phdr.p_filesz);
|
||||
|
||||
if (fseek(xbl, xbl_core_phdr.p_offset, SEEK_SET) != 0) {
|
||||
fprintf(stderr, "Failed to seek to offset 0x%lx\n", xbl_core_phdr.p_offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fread(buf, xbl_core_phdr.p_filesz, 1, xbl) != 1) {
|
||||
fprintf(stderr, "Failed to read %lu bytes\n", xbl_core_phdr.p_filesz);
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_part(out, xbl_core_phdr.p_paddr, buf, xbl_core_phdr.p_filesz);
|
||||
|
||||
fclose(out);
|
||||
|
||||
/*
|
||||
* Write out the xbl_sec.mbn blob (it contains it's own embedded ELF header so we don't need
|
||||
* to add one).
|
||||
*/
|
||||
out = fopen("xbl_sec.mbn", "wb");
|
||||
if (!out) {
|
||||
fprintf(stderr, "Failed to open xbl_sec.mbn for writing\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = malloc(xbl_sec_phdr.p_filesz);
|
||||
|
||||
if (fseek(xbl, xbl_sec_phdr.p_offset, SEEK_SET) != 0) {
|
||||
fprintf(stderr, "Failed to seek to offset 0x%lx\n", xbl_sec_phdr.p_offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fread(buf, xbl_sec_phdr.p_filesz, 1, xbl) != 1) {
|
||||
fprintf(stderr, "Failed to read %lu bytes\n", xbl_sec_phdr.p_filesz);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fwrite(buf, xbl_sec_phdr.p_filesz, 1, out);
|
||||
|
||||
fflush(out);
|
||||
fclose(out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int optflag, ret;
|
||||
char *xbl_path = NULL;
|
||||
|
||||
if (argc < 2)
|
||||
return usage();
|
||||
|
||||
while ((optflag = getopt(argc, argv, "v")) != -1) {
|
||||
switch (optflag) {
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default:
|
||||
return usage();
|
||||
}
|
||||
}
|
||||
|
||||
xbl_path = argv[optind];
|
||||
if (!xbl_path)
|
||||
return usage();
|
||||
|
||||
FILE *xbl = fopen(xbl_path, "rb");
|
||||
if (!xbl) {
|
||||
fprintf(stderr, "Failed to open %s\n", xbl_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = unpack_xbl(xbl);
|
||||
printf("\nAll done!\n");
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
Reference in New Issue
Block a user