summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/util/zbin.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/ipxe/src/util/zbin.c')
-rw-r--r--qemu/roms/ipxe/src/util/zbin.c96
1 files changed, 86 insertions, 10 deletions
diff --git a/qemu/roms/ipxe/src/util/zbin.c b/qemu/roms/ipxe/src/util/zbin.c
index 3b7cf95b3..1862a3827 100644
--- a/qemu/roms/ipxe/src/util/zbin.c
+++ b/qemu/roms/ipxe/src/util/zbin.c
@@ -1,13 +1,21 @@
+#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
#include <sys/stat.h>
-
-#define ENCODE
-#define VERBOSE
-#include "nrv2b.c"
-FILE *infile, *outfile;
+#include <lzma.h>
#define DEBUG 0
+/* LZMA filter choices. Must match those used by unlzma.S */
+#define LZMA_LC 2
+#define LZMA_LP 0
+#define LZMA_PB 0
+
+/* LZMA preset choice. This is a policy decision */
+#define LZMA_PRESET ( LZMA_PRESET_DEFAULT | LZMA_PRESET_EXTREME )
+
struct input_file {
void *buf;
size_t len;
@@ -177,13 +185,75 @@ static int process_zinfo_copy ( struct input_file *input,
return 0;
}
+#define OPCODE_CALL 0xe8
+#define OPCODE_JMP 0xe9
+
+static void bcj_filter ( void *data, size_t len ) {
+ struct {
+ uint8_t opcode;
+ int32_t target;
+ } __attribute__ (( packed )) *jump;
+ ssize_t limit = ( len - sizeof ( *jump ) );
+ ssize_t offset;
+
+ /* liblzma does include an x86 BCJ filter, but it's hideously
+ * convoluted and undocumented. This BCJ filter is
+ * substantially simpler and achieves the same compression (at
+ * the cost of requiring the decompressor to know the size of
+ * the decompressed data, which we already have in iPXE).
+ */
+ for ( offset = 0 ; offset <= limit ; offset++ ) {
+ jump = ( data + offset );
+
+ /* Skip instructions that are not followed by a rel32 address */
+ if ( ( jump->opcode != OPCODE_CALL ) &&
+ ( jump->opcode != OPCODE_JMP ) )
+ continue;
+
+ /* Convert rel32 address to an absolute address. To
+ * avoid false positives (which damage the compression
+ * ratio), we should check that the jump target is
+ * within the range [0,limit).
+ *
+ * Some output values would then end up being mapped
+ * from two distinct input values, making the
+ * transformation irreversible. To solve this, we
+ * transform such values back into the part of the
+ * range which would otherwise correspond to no input
+ * values.
+ */
+ if ( ( jump->target >= -offset ) &&
+ ( jump->target < ( limit - offset ) ) ) {
+ /* Convert relative addresses in the range
+ * [-offset,limit-offset) to absolute
+ * addresses in the range [0,limit).
+ */
+ jump->target += offset;
+ } else if ( ( jump->target >= ( limit - offset ) ) &&
+ ( jump->target < limit ) ) {
+ /* Convert positive numbers in the range
+ * [limit-offset,limit) to negative numbers in
+ * the range [-offset,0).
+ */
+ jump->target -= limit;
+ }
+ offset += sizeof ( jump->target );
+ };
+}
+
static int process_zinfo_pack ( struct input_file *input,
struct output_file *output,
union zinfo_record *zinfo ) {
struct zinfo_pack *pack = &zinfo->pack;
size_t offset = pack->offset;
size_t len = pack->len;
- unsigned long packed_len;
+ size_t packed_len = 0;
+ size_t remaining = ( output->max_len - output->len );
+ lzma_options_lzma options;
+ const lzma_filter filters[] = {
+ { .id = LZMA_FILTER_LZMA1, .options = &options },
+ { .id = LZMA_VLI_UNKNOWN }
+ };
if ( ( offset + len ) > input->len ) {
fprintf ( stderr, "Input buffer overrun on pack\n" );
@@ -196,9 +266,15 @@ static int process_zinfo_pack ( struct input_file *input,
return -1;
}
- if ( ucl_nrv2b_99_compress ( ( input->buf + offset ), len,
- ( output->buf + output->len ),
- &packed_len, 0 ) != UCL_E_OK ) {
+ bcj_filter ( ( input->buf + offset ), len );
+
+ lzma_lzma_preset ( &options, LZMA_PRESET );
+ options.lc = LZMA_LC;
+ options.lp = LZMA_LP;
+ options.pb = LZMA_PB;
+ if ( lzma_raw_buffer_encode ( filters, NULL, ( input->buf + offset ),
+ len, ( output->buf + output->len ),
+ &packed_len, remaining ) != LZMA_OK ) {
fprintf ( stderr, "Compression failure\n" );
return -1;
}
@@ -206,7 +282,7 @@ static int process_zinfo_pack ( struct input_file *input,
if ( DEBUG ) {
fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n",
offset, ( offset + len ), output->len,
- ( size_t )( output->len + packed_len ) );
+ ( output->len + packed_len ) );
}
output->len += packed_len;