I have a program that attempts to create a binary lump file, which means it basically consists of metadata followed by raw, uncompressed file data Simply put, the header contains 8 pad bytes, a 4 byte LE value for the file count, a 4 byte LE value for the size (this is an old format meant for sizes < 4 GB), and then a series of file metadata entries consisting of a 4 byte LE value for the file offset, then a 32 character value denoting the file path.
In an attempt to keep this portable and simplistic, I tried to stick to the standard library (libc, MSVCRT, etc), with an exception to a header from the C++ Boost library consisting solely of macros that determine machine endianness.
It compiles fine and runs okay up to the point where the raw file data is output, which triggers a segfault. Naturally, I found this out by debugging. However, something happened that baffles me in regards to what GDB was showing. I commented out the for-loop that inserts the file data in order to assess if at least the metadata was coming out okay.
It still segfaulted, so I went into GDB again, breakpointed the suspect function, ran it, and began stepping through the function line-by-line. The commented out for-loop was still in the program code!
I tried resaving the code file, deleting the output executable and object files, and even stripped out the commented out for-loop entirely. All three attempts yielded the same for-loop I supposedly commented out.
I really hope someone can spot something in my code that would cause this. I'd hate to be faced with the possibility of this being an internal compiler bug. GCC is needed to build, because this uses its __builtin_bswapXX functions.
Code:
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <boost/detail/endian.hpp>
#define PAC_INIT_LEN 16
#define PAC_PATH_LEN 32
#define PAC_ITEM_META_LEN 36
struct _pac_item {
uint8_t *buffer;
char path[PAC_PATH_LEN];
uint32_t offset;
};
typedef struct _pac {
struct _pac_item *items;
uint32_t count;
uint32_t size;
} pac_t;
pac_t* pac_new() {
pac_t *p = (pac_t *)malloc(sizeof(pac_t));
p->count = 0;
p->size = PAC_INIT_LEN;
p->items = NULL;
return p;
}
void pac_info(const pac_t *p) {
if (p) {
printf("Files: %u (%X)\n", p->count, p->count);
printf("Size: %u (%X)\n", p->size, p->size);
if (p->items) {
printf("File offsets: ");
for (int i = 0; i < p->count; i++)
printf("%u (%X) ", p->items[i].offset, p->items[i].offset);
printf("\nFile paths: ");
for (int i = 0; i < p->count; i++)
printf("%s ", p->items[i].path);
printf("\n");
}
} else
printf("PAC not initialised\n");
}
void pac_add(pac_t *p, char *fp) {
if (p) {
struct stat *si = (struct stat *)malloc(sizeof(struct stat));
stat(fp, si);
p->size += PAC_ITEM_META_LEN;
p->count++;
if (p->items)
p->items = (struct _pac_item *)realloc(p->items, sizeof(struct _pac_item) * p->count);
else
p->items = (struct _pac_item *)malloc(sizeof(struct _pac_item) * p->count);
p->items[p->count-1].offset = p->size;
#ifdef _WIN32
for (int i = 0; i < PAC_PATH_LEN; i++)
if (fp[i] == '\\')
fp[i] = '/';
#endif // _WIN32
if (strlen(fp) <= PAC_PATH_LEN) {
memset(p->items[p->count-1].path, 0, PAC_PATH_LEN);
strncpy(p->items[p->count-1].path, fp, PAC_PATH_LEN);
}
FILE * f = fopen(fp, "r+b");
p->items[p->count-1].buffer = (uint8_t *)malloc(si->st_size);
fread((void *)p->items[p->count-1].buffer, 1, si->st_size, f);
fclose(f);
p->size += si->st_size;
free(si);
}
}
uint8_t* pac_to_buffer(const pac_t *p) {
if (p) {
uint8_t *buf = (uint8_t *)malloc(p->size);
uint8_t *pos = buf;
#ifdef BOOST_BIG_ENDIAN
uint32_t count_le = __builtin_bswap32(p->count);
uint32_t size_le = __builtin_bswap32(p->size);
#else
uint32_t count_le = p->count;
uint32_t size_le = p->size;
#endif // BOOST_BIG_ENDIAN
uint32_t offset_le;
memset((void *)buf, 0, 8);
pos += 8;
memcpy((void *)pos, &count_le, 4);
pos += 4;
memcpy((void *)pos, &size_le, 4);
pos += 4;
for (int i = 0; i < p->count; i++) {
#ifdef BOOST_BIG_ENDIAN
offset_le = __builtin_bswap32(p->items[i].offset);
#else
offset_le = p->items[i].offset;
#endif // BOOST_BIG_ENDIAN
memcpy((void *)buf, &offset_le, 4);
memcpy((void *)buf, (void *)p->items[i].path, PAC_PATH_LEN);
}
/*for (int i = 0; i < p->count; i++) {
uint32_t bufsize = (i == p->count-1 ? (p->size - p->items[i].offset) : (p->items[i+1].offset - p->items[i].offset));
memcpy((void *)buf, p->items[i].buffer, bufsize);
}*/
} else
return NULL;
}
void pac_free(pac_t *p) {
if (p) {
for (int i = 0; i < p->count; i++)
free(p->items[i].buffer);
free(p->items);
free(p);
}
}
int main() {
FILE *f = fopen("test.pac", "w+b");
pac_t *p = pac_new();
pac_add(p, "L9jUC1T.png");
pac_info(p);
fwrite(pac_to_buffer(p), 1, p->size, f);
pac_free(p);
fclose(f);
return 0;
}