Rev 14 | Rev 16 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | // ifstool.c -- portable reimplementation of QNX's mkifs by Pierre-Marie Baty <pm@pmbaty.com> |
2 | |||
12 | pmbaty | 3 | // TODO: preboot file stripping |
4 | // TODO: startup file stripping |
||
5 | // TODO: kernel file stripping |
||
6 | // TODO: boot script compiler |
||
7 | |||
1 | pmbaty | 8 | #include <stdint.h> |
9 | #include <stdbool.h> |
||
10 | #include <stdlib.h> |
||
2 | pmbaty | 11 | #include <stdarg.h> |
1 | pmbaty | 12 | #include <stdio.h> |
13 | #include <string.h> |
||
14 | #include <errno.h> |
||
15 | #include <sys/stat.h> |
||
16 | #include <ctype.h> |
||
17 | #include <time.h> |
||
18 | |||
19 | |||
11 | pmbaty | 20 | // compiler-specific glue |
1 | pmbaty | 21 | #ifdef _MSC_VER |
22 | #include <io.h> |
||
14 | pmbaty | 23 | #include <direct.h> |
24 | #include <sys/utime.h> |
||
15 | pmbaty | 25 | #include <process.h> |
7 | pmbaty | 26 | #define __x86_64__ 1 |
5 | pmbaty | 27 | #define __ORDER_BIG_ENDIAN__ 4321 |
1 | pmbaty | 28 | #define __ORDER_LITTLE_ENDIAN__ 1234 |
29 | #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ |
||
30 | #define __attribute__(x) |
||
7 | pmbaty | 31 | #define __builtin_bswap16(x) _byteswap_ushort ((unsigned short) (x)) |
5 | pmbaty | 32 | #define __builtin_bswap32(x) _byteswap_ulong ((unsigned long) (x)) |
1 | pmbaty | 33 | #define __builtin_bswap64(x) _byteswap_uint64 ((unsigned long long) (x)) |
34 | #define S_IFIFO 0x1000 |
||
35 | #define S_IFLNK 0xa000 |
||
36 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) |
||
37 | #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) |
||
38 | #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) |
||
39 | #define strdup(s) _strdup ((s)) |
||
2 | pmbaty | 40 | #define strcasecmp(s1,s2) _stricmp ((s1), (s2)) |
1 | pmbaty | 41 | #define fseek(fp,off,m) _fseeki64 ((fp), (off), (m)) |
42 | #define access(p,m) _access ((p), (m)) |
||
14 | pmbaty | 43 | #define mkdir(p,m) _mkdir ((p)) |
44 | #define chmod(p,m) _chmod ((p), (m)) |
||
15 | pmbaty | 45 | #define unlink(p) _unlink ((p)) |
14 | pmbaty | 46 | #define utimbuf __utimbuf32 |
47 | #define utime(p,t) _utime32 ((p), (t)) |
||
1 | pmbaty | 48 | #define MAXPATHLEN 1024 |
8 | pmbaty | 49 | #ifndef thread_local |
50 | #define thread_local __declspec(thread) // the thread_local keyword wasn't defined before C++11 and C23 |
||
51 | #endif // !thread_local |
||
11 | pmbaty | 52 | #define START_OF_PACKED_STRUCT() __pragma(pack(push)) __pragma(pack(1)) |
53 | #define END_OF_PACKED_STRUCT() __pragma(pack(pop)) |
||
54 | #define PACKED(thing) thing |
||
1 | pmbaty | 55 | #else // !_MSC_VER |
56 | #include <sys/param.h> |
||
57 | #include <unistd.h> |
||
14 | pmbaty | 58 | #include <utime.h> |
8 | pmbaty | 59 | #ifndef thread_local |
60 | #define thread_local __thread // the thread_local keyword wasn't defined before C++11 and C23 |
||
61 | #endif // !thread_local |
||
11 | pmbaty | 62 | #define START_OF_PACKED_STRUCT() |
63 | #define END_OF_PACKED_STRUCT() |
||
64 | #define PACKED(thing) thing __attribute__((packed)) |
||
1 | pmbaty | 65 | #endif // _MSC_VER |
66 | |||
67 | |||
4 | pmbaty | 68 | // handy macros that generate a version number in the format "YYYYMMDD" corresponding to the build date. Usage: printf ("version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD); |
7 | pmbaty | 69 | #ifndef VERSION_ARG_YYYYMMDD |
4 | pmbaty | 70 | #define BUILDDATE_YEAR (&__DATE__[7]) // compiler will optimize this into a const string, e.g. "2021" |
11 | pmbaty | 71 | #define BUILDDATE_MONTH ( \ |
72 | *((uint32_t *) __DATE__) == *((uint32_t *) "Jan ") ? "01" : \ |
||
73 | *((uint32_t *) __DATE__) == *((uint32_t *) "Feb ") ? "02" : \ |
||
74 | *((uint32_t *) __DATE__) == *((uint32_t *) "Mar ") ? "03" : \ |
||
75 | *((uint32_t *) __DATE__) == *((uint32_t *) "Apr ") ? "04" : \ |
||
76 | *((uint32_t *) __DATE__) == *((uint32_t *) "May ") ? "05" : \ |
||
77 | *((uint32_t *) __DATE__) == *((uint32_t *) "Jun ") ? "06" : \ |
||
78 | *((uint32_t *) __DATE__) == *((uint32_t *) "Jul ") ? "07" : \ |
||
79 | *((uint32_t *) __DATE__) == *((uint32_t *) "Aug ") ? "08" : \ |
||
80 | *((uint32_t *) __DATE__) == *((uint32_t *) "Sep ") ? "09" : \ |
||
81 | *((uint32_t *) __DATE__) == *((uint32_t *) "Oct ") ? "10" : \ |
||
82 | *((uint32_t *) __DATE__) == *((uint32_t *) "Nov ") ? "11" : \ |
||
83 | *((uint32_t *) __DATE__) == *((uint32_t *) "Dec ") ? "12" : \ |
||
84 | "XX" \ |
||
85 | ) // compiler will optimize this into a const string, e.g. "11" |
||
86 | #define BUILDDATE_DAY ( \ |
||
87 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 1 ") ? "01" : \ |
||
88 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 2 ") ? "02" : \ |
||
89 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 3 ") ? "03" : \ |
||
90 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 4 ") ? "04" : \ |
||
91 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 5 ") ? "05" : \ |
||
92 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 6 ") ? "06" : \ |
||
93 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 7 ") ? "07" : \ |
||
94 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 8 ") ? "08" : \ |
||
95 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 9 ") ? "09" : \ |
||
96 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 10 ") ? "10" : \ |
||
97 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 11 ") ? "11" : \ |
||
98 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 12 ") ? "12" : \ |
||
99 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 13 ") ? "13" : \ |
||
100 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 14 ") ? "14" : \ |
||
101 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 15 ") ? "15" : \ |
||
102 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 16 ") ? "16" : \ |
||
103 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 17 ") ? "17" : \ |
||
104 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 18 ") ? "18" : \ |
||
105 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 19 ") ? "19" : \ |
||
106 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 20 ") ? "20" : \ |
||
107 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 21 ") ? "21" : \ |
||
108 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 22 ") ? "22" : \ |
||
109 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 23 ") ? "23" : \ |
||
110 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 24 ") ? "24" : \ |
||
111 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 25 ") ? "25" : \ |
||
112 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 26 ") ? "26" : \ |
||
113 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 27 ") ? "27" : \ |
||
114 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 28 ") ? "28" : \ |
||
115 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 29 ") ? "29" : \ |
||
116 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 30 ") ? "30" : \ |
||
117 | *((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 31 ") ? "31" : \ |
||
118 | "XX" \ |
||
119 | ) // compiler will optimize this into a const string, e.g. "14" |
||
14 | pmbaty | 120 | #define VERSION_FMT_YYYYMMDD "%s%s%s" |
4 | pmbaty | 121 | #define VERSION_ARG_YYYYMMDD BUILDDATE_YEAR, BUILDDATE_MONTH, BUILDDATE_DAY |
7 | pmbaty | 122 | #endif // !VERSION_ARG_YYYYMMDD |
4 | pmbaty | 123 | |
124 | |||
11 | pmbaty | 125 | // macro to bring __FILE_NAME__ support to moronic compilers |
126 | #ifndef __FILE_NAME__ // Clang 9+ has the macro, GCC 12+ added it too in 2021, MSVC obviously won't do it. Heh. |
||
127 | #define __FILE_NAME__ ( \ |
||
15 | pmbaty | 128 | (sizeof (__FILE__) > 2) && ((__FILE__[sizeof (__FILE__) - 2] == '/') || (__FILE__[sizeof (__FILE__) - 2] == '\\')) ? &__FILE__[sizeof (__FILE__) - 1] : \ |
129 | (sizeof (__FILE__) > 3) && ((__FILE__[sizeof (__FILE__) - 3] == '/') || (__FILE__[sizeof (__FILE__) - 3] == '\\')) ? &__FILE__[sizeof (__FILE__) - 2] : \ |
||
130 | (sizeof (__FILE__) > 4) && ((__FILE__[sizeof (__FILE__) - 4] == '/') || (__FILE__[sizeof (__FILE__) - 4] == '\\')) ? &__FILE__[sizeof (__FILE__) - 3] : \ |
||
131 | (sizeof (__FILE__) > 5) && ((__FILE__[sizeof (__FILE__) - 5] == '/') || (__FILE__[sizeof (__FILE__) - 5] == '\\')) ? &__FILE__[sizeof (__FILE__) - 4] : \ |
||
132 | (sizeof (__FILE__) > 6) && ((__FILE__[sizeof (__FILE__) - 6] == '/') || (__FILE__[sizeof (__FILE__) - 6] == '\\')) ? &__FILE__[sizeof (__FILE__) - 5] : \ |
||
133 | (sizeof (__FILE__) > 7) && ((__FILE__[sizeof (__FILE__) - 7] == '/') || (__FILE__[sizeof (__FILE__) - 7] == '\\')) ? &__FILE__[sizeof (__FILE__) - 6] : \ |
||
134 | (sizeof (__FILE__) > 8) && ((__FILE__[sizeof (__FILE__) - 8] == '/') || (__FILE__[sizeof (__FILE__) - 8] == '\\')) ? &__FILE__[sizeof (__FILE__) - 7] : \ |
||
135 | (sizeof (__FILE__) > 9) && ((__FILE__[sizeof (__FILE__) - 9] == '/') || (__FILE__[sizeof (__FILE__) - 9] == '\\')) ? &__FILE__[sizeof (__FILE__) - 8] : \ |
||
136 | (sizeof (__FILE__) > 10) && ((__FILE__[sizeof (__FILE__) - 10] == '/') || (__FILE__[sizeof (__FILE__) - 10] == '\\')) ? &__FILE__[sizeof (__FILE__) - 9] : \ |
||
137 | (sizeof (__FILE__) > 11) && ((__FILE__[sizeof (__FILE__) - 11] == '/') || (__FILE__[sizeof (__FILE__) - 11] == '\\')) ? &__FILE__[sizeof (__FILE__) - 10] : \ |
||
138 | (sizeof (__FILE__) > 12) && ((__FILE__[sizeof (__FILE__) - 12] == '/') || (__FILE__[sizeof (__FILE__) - 12] == '\\')) ? &__FILE__[sizeof (__FILE__) - 11] : \ |
||
139 | (sizeof (__FILE__) > 13) && ((__FILE__[sizeof (__FILE__) - 13] == '/') || (__FILE__[sizeof (__FILE__) - 13] == '\\')) ? &__FILE__[sizeof (__FILE__) - 12] : \ |
||
140 | (sizeof (__FILE__) > 14) && ((__FILE__[sizeof (__FILE__) - 14] == '/') || (__FILE__[sizeof (__FILE__) - 14] == '\\')) ? &__FILE__[sizeof (__FILE__) - 13] : \ |
||
141 | (sizeof (__FILE__) > 15) && ((__FILE__[sizeof (__FILE__) - 15] == '/') || (__FILE__[sizeof (__FILE__) - 15] == '\\')) ? &__FILE__[sizeof (__FILE__) - 14] : \ |
||
142 | (sizeof (__FILE__) > 16) && ((__FILE__[sizeof (__FILE__) - 16] == '/') || (__FILE__[sizeof (__FILE__) - 16] == '\\')) ? &__FILE__[sizeof (__FILE__) - 15] : \ |
||
143 | (sizeof (__FILE__) > 17) && ((__FILE__[sizeof (__FILE__) - 17] == '/') || (__FILE__[sizeof (__FILE__) - 17] == '\\')) ? &__FILE__[sizeof (__FILE__) - 16] : \ |
||
144 | (sizeof (__FILE__) > 18) && ((__FILE__[sizeof (__FILE__) - 18] == '/') || (__FILE__[sizeof (__FILE__) - 18] == '\\')) ? &__FILE__[sizeof (__FILE__) - 17] : \ |
||
145 | (sizeof (__FILE__) > 19) && ((__FILE__[sizeof (__FILE__) - 19] == '/') || (__FILE__[sizeof (__FILE__) - 19] == '\\')) ? &__FILE__[sizeof (__FILE__) - 18] : \ |
||
146 | (sizeof (__FILE__) > 20) && ((__FILE__[sizeof (__FILE__) - 20] == '/') || (__FILE__[sizeof (__FILE__) - 20] == '\\')) ? &__FILE__[sizeof (__FILE__) - 19] : \ |
||
147 | (sizeof (__FILE__) > 21) && ((__FILE__[sizeof (__FILE__) - 21] == '/') || (__FILE__[sizeof (__FILE__) - 21] == '\\')) ? &__FILE__[sizeof (__FILE__) - 20] : \ |
||
148 | (sizeof (__FILE__) > 22) && ((__FILE__[sizeof (__FILE__) - 22] == '/') || (__FILE__[sizeof (__FILE__) - 22] == '\\')) ? &__FILE__[sizeof (__FILE__) - 21] : \ |
||
149 | (sizeof (__FILE__) > 23) && ((__FILE__[sizeof (__FILE__) - 23] == '/') || (__FILE__[sizeof (__FILE__) - 23] == '\\')) ? &__FILE__[sizeof (__FILE__) - 22] : \ |
||
150 | (sizeof (__FILE__) > 24) && ((__FILE__[sizeof (__FILE__) - 24] == '/') || (__FILE__[sizeof (__FILE__) - 24] == '\\')) ? &__FILE__[sizeof (__FILE__) - 23] : \ |
||
151 | (sizeof (__FILE__) > 25) && ((__FILE__[sizeof (__FILE__) - 25] == '/') || (__FILE__[sizeof (__FILE__) - 25] == '\\')) ? &__FILE__[sizeof (__FILE__) - 24] : \ |
||
152 | (sizeof (__FILE__) > 26) && ((__FILE__[sizeof (__FILE__) - 26] == '/') || (__FILE__[sizeof (__FILE__) - 26] == '\\')) ? &__FILE__[sizeof (__FILE__) - 25] : \ |
||
153 | (sizeof (__FILE__) > 27) && ((__FILE__[sizeof (__FILE__) - 27] == '/') || (__FILE__[sizeof (__FILE__) - 27] == '\\')) ? &__FILE__[sizeof (__FILE__) - 26] : \ |
||
154 | (sizeof (__FILE__) > 28) && ((__FILE__[sizeof (__FILE__) - 28] == '/') || (__FILE__[sizeof (__FILE__) - 28] == '\\')) ? &__FILE__[sizeof (__FILE__) - 27] : \ |
||
155 | (sizeof (__FILE__) > 29) && ((__FILE__[sizeof (__FILE__) - 29] == '/') || (__FILE__[sizeof (__FILE__) - 29] == '\\')) ? &__FILE__[sizeof (__FILE__) - 28] : \ |
||
156 | (sizeof (__FILE__) > 30) && ((__FILE__[sizeof (__FILE__) - 30] == '/') || (__FILE__[sizeof (__FILE__) - 30] == '\\')) ? &__FILE__[sizeof (__FILE__) - 29] : \ |
||
157 | (sizeof (__FILE__) > 31) && ((__FILE__[sizeof (__FILE__) - 31] == '/') || (__FILE__[sizeof (__FILE__) - 31] == '\\')) ? &__FILE__[sizeof (__FILE__) - 30] : \ |
||
158 | (sizeof (__FILE__) > 32) && ((__FILE__[sizeof (__FILE__) - 32] == '/') || (__FILE__[sizeof (__FILE__) - 32] == '\\')) ? &__FILE__[sizeof (__FILE__) - 31] : \ |
||
159 | (sizeof (__FILE__) > 33) && ((__FILE__[sizeof (__FILE__) - 33] == '/') || (__FILE__[sizeof (__FILE__) - 33] == '\\')) ? &__FILE__[sizeof (__FILE__) - 32] : \ |
||
11 | pmbaty | 160 | __FILE__) // this *COMPILE-TIME* macro complements the __FILE__ macro defined by the C standard by returning just the filename portion of the full path. Supports filenames up to 32 chars. Expand as necessary. |
161 | #endif // !__FILE_NAME__ |
||
8 | pmbaty | 162 | |
163 | |||
15 | pmbaty | 164 | // logging macros |
165 | #define LOG(type,lvl,...) do { if ((lvl) <= verbose_level) { fprintf (stderr, "ifstool: %s: ", (type)); if (verbose_level > 1) fprintf (stderr, "%s:%d:%s(): ", __FILE_NAME__, __LINE__, __FUNCTION__); fprintf (stderr, __VA_ARGS__); fputc ('\n', stderr); } } while (0) |
||
166 | #define LOG_ERROR(...) LOG ("error", 0, __VA_ARGS__) |
||
167 | #define LOG_WARNING(...) LOG ("warning", 1, __VA_ARGS__) |
||
168 | #define LOG_INFO(...) LOG ("info", 2, __VA_ARGS__) |
||
169 | #define LOG_DEBUG(...) LOG ("debug", 3, __VA_ARGS__) |
||
170 | |||
171 | // macro to gently exit with an error message |
||
172 | #define DIE_WITH_EXITCODE(exitcode,...) do { LOG_ERROR (__VA_ARGS__); exit ((exitcode)); } while (0) |
||
173 | |||
11 | pmbaty | 174 | // macro to exit less brutally than with abort() if something doesn't go the way we'd like to |
15 | pmbaty | 175 | #define WELLMANNERED_ASSERT(is_is_true,...) do { if (!(is_is_true)) { LOG ("fatal error", 0, "consistency check failed"); LOG ("fatal error", 0, __VA_ARGS__); exit (1); } } while (0) |
8 | pmbaty | 176 | |
177 | |||
11 | pmbaty | 178 | // macros for checked read/write/seek operations |
179 | #define fseek_or_die(fp,pos,mode) WELLMANNERED_ASSERT (fseek ((fp), (pos), (mode)) == 0, "fseek() failed with errno %d (%s)", errno, strerror (errno)) |
||
180 | #define fread_or_die(buf,sz,len,fp) WELLMANNERED_ASSERT (fread ((buf), (sz), (len), (fp)) == (len), "fread() failed with errno %d (%s)", errno, strerror (errno)) |
||
181 | #define fwrite_or_die(buf,sz,len,fp) WELLMANNERED_ASSERT ((fwrite ((buf), (sz), (len), (fp)) == (len)) && (fflush ((fp)) == 0), "flushed fwrite() failed with errno %d (%s)", errno, strerror (errno)) |
||
8 | pmbaty | 182 | |
183 | |||
9 | pmbaty | 184 | // macros for accessing ELF files |
7 | pmbaty | 185 | #define ELF_MAGIC_STR "\x7f" "ELF" |
10 | pmbaty | 186 | #define ELF_ENDIAN_LITTLE 1 // 'endianness' member of an ELF header: ELF file is little endian |
187 | #define ELF_ENDIAN_BIG 2 // 'endianness' member of an ELF header: ELF file is big endian |
||
188 | #define ELF_MACHINE_X86_64 0x3e // 'instruction_set' member of an ELF header, also used in the IFS startup header: ELF file is for x86_64 processors (62 decimal) |
||
189 | #define ELF_MACHINE_AARCH64 0xb7 // 'instruction_set' member of an ELF header, also used in the IFS startup header: ELF file is for ARM64 processors (183 decimal) |
||
190 | #define ELF_SECTIONTYPE_STRINGTABLE 3 |
||
7 | pmbaty | 191 | #define ELF_DT_NULL 0 // marks end of dynamic section |
192 | #define ELF_DT_SONAME 14 // canonical name of shared object |
||
10 | pmbaty | 193 | #define ELF_GET_NUMERIC(elfhdr,elfstruct,member) ((elfhdr)->u.elf.platform_size == 2 ? /* is it a 64-bit ELF file ? */ \ |
9 | pmbaty | 194 | ( \ |
195 | (sizeof ((elfstruct)->u.elf64.member) == 1) || (((elfhdr)->u.elf.endianness == 1) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || (((elfhdr)->u.elf.endianness == 2) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) ? /* single-byte, or same endianness ? */ \ |
||
196 | (elfstruct)->u.elf64.member /* same endianness, or single byte required: don't swap */ \ |
||
197 | : /* else */ \ |
||
198 | (sizeof ((elfstruct)->u.elf64.member) == 8 ? __builtin_bswap64 ((elfstruct)->u.elf64.member) : (sizeof ((elfstruct)->u.elf64.member) == 4 ? __builtin_bswap32 ((elfstruct)->u.elf64.member) : __builtin_bswap16 ((elfstruct)->u.elf64.member))) /* different endianness: swap */ \ |
||
199 | ) \ |
||
200 | : /* else peek at 32-bit ELF */ \ |
||
201 | ( \ |
||
202 | (sizeof ((elfstruct)->u.elf32.member) == 1) || (((elfhdr)->u.elf.endianness == 1) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || (((elfhdr)->u.elf.endianness == 2) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) ? /* single-byte, or same endianness ? */ \ |
||
203 | (elfstruct)->u.elf32.member /* same endianness, or single byte required: don't swap */ \ |
||
204 | : /* else */ \ |
||
205 | (sizeof ((elfstruct)->u.elf32.member) == 4 ? __builtin_bswap32 ((elfstruct)->u.elf32.member) : __builtin_bswap16 ((elfstruct)->u.elf32.member)) /* different endianness: swap */ \ |
||
206 | ) \ |
||
207 | ) // this macro supports 32- and 64-bit ELF files in low and big endianness transparently |
||
10 | pmbaty | 208 | #define ELF_SET_NUMERIC(elfhdr,elfstruct,member,data) ((elfhdr)->u.elf.platform_size == 2 ? /* is it a 64-bit ELF file ? */ \ |
209 | ((elfstruct)->u.elf64.member = ( \ |
||
210 | (sizeof ((elfstruct)->u.elf64.member) == 1) || (((elfhdr)->u.elf.endianness == 1) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || (((elfhdr)->u.elf.endianness == 2) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) ? /* single-byte, or same endianness ? */ \ |
||
211 | (sizeof ((elfstruct)->u.elf64.member) == 8 ? (uint64_t) ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 4 ? (uint32_t) ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 2 ? (uint16_t) ((data)) : (uint8_t) ((data))))) /* same endianness, or single byte required: don't swap */ \ |
||
212 | : /* else */ \ |
||
213 | (sizeof ((elfstruct)->u.elf64.member) == 8 ? __builtin_bswap64 ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 4 ? __builtin_bswap32 ((data)) : __builtin_bswap16 ((data)))) /* different endianness: swap */ \ |
||
214 | )) \ |
||
215 | : /* else poke at 32-bit ELF */ \ |
||
216 | ((elfstruct)->u.elf32.member = ( \ |
||
217 | (sizeof ((elfstruct)->u.elf32.member) == 1) || (((elfhdr)->u.elf.endianness == 1) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || (((elfhdr)->u.elf.endianness == 2) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) ? /* single-byte, or same endianness ? */ \ |
||
218 | (sizeof ((elfstruct)->u.elf64.member) == 4 ? (uint32_t) ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 2 ? (uint16_t) ((data)) : (uint8_t) ((data)))) /* same endianness, or single byte required: don't swap */ \ |
||
219 | : /* else */ \ |
||
220 | (sizeof ((elfstruct)->u.elf32.member) == 4 ? __builtin_bswap32 ((data)) : __builtin_bswap16 ((data))) /* different endianness: swap */ \ |
||
221 | )) \ |
||
222 | ) // this macro supports 32- and 64-bit ELF files in low and big endianness transparently |
||
223 | #define ELF_GET_STRING(elfhdr,elfstruct,member) ((elfhdr)->u.elf.platform_size == 2 ? (elfstruct)->u.elf64.member : (elfstruct)->u.elf32.member) // this macro supports 32- and 64-bit ELF files transparently |
||
224 | #define ELF_SET_STRING(elfhdr,elfstruct,member,data,len) memcpy (((elfhdr)->u.elf.platform_size == 2 ? (elfstruct)->u.elf64.member : (elfstruct)->u.elf32.member), (data), (len)) // this macro supports 32- and 64-bit ELF files transparently |
||
9 | pmbaty | 225 | #define ELF_STRUCT_SIZE(elfhdr,elfstruct) ((elfhdr)->u.elf.platform_size == 2 ? sizeof ((elfstruct)->u.elf64) : sizeof ((elfstruct)->u.elf32)) // this macro supports 32- and 64-bit ELF files transparently |
7 | pmbaty | 226 | |
10 | pmbaty | 227 | |
11 | pmbaty | 228 | // placeholder value |
229 | #define WILL_BE_FILLED_LATER 0xbaadf00d // urgh |
||
10 | pmbaty | 230 | |
1 | pmbaty | 231 | |
232 | // bitmapped flags used in the flags1 member of the startup header |
||
233 | #define STARTUP_HDR_FLAGS1_VIRTUAL (1 << 0) |
||
234 | #define STARTUP_HDR_FLAGS1_BIGENDIAN (1 << 1) |
||
235 | //#define STARTUP_HDR_FLAGS1_COMPRESS_MASK 0x1c |
||
236 | //#define STARTUP_HDR_FLAGS1_COMPRESS_SHIFT 0x02 |
||
237 | //#define STARTUP_HDR_FLAGS1_COMPRESS_NONE 0x00 |
||
238 | //#define STARTUP_HDR_FLAGS1_COMPRESS_ZLIB 0x04 |
||
239 | //#define STARTUP_HDR_FLAGS1_COMPRESS_LZO 0x08 |
||
240 | //#define STARTUP_HDR_FLAGS1_COMPRESS_UCL 0x0c |
||
241 | #define STARTUP_HDR_FLAGS1_TRAILER_V2 (1 << 5) // if set, then a struct startup_trailer_v2 follows the startup. If the image is compressed, then the compressed imagefs is followed by a struct image_trailer_v2 |
||
242 | |||
243 | |||
244 | // bitmapped flags used in the flags member of the image header |
||
245 | #define IMAGE_FLAGS_BIGENDIAN (1 << 0) // header, trailer, dirents in big-endian format |
||
246 | #define IMAGE_FLAGS_READONLY (1 << 1) // do not try to write to image (rom/flash) |
||
247 | #define IMAGE_FLAGS_INO_BITS (1 << 2) // inode bits valid |
||
248 | #define IMAGE_FLAGS_SORTED (1 << 3) // dirent section is sorted (by pathname) |
||
249 | #define IMAGE_FLAGS_TRAILER_V2 (1 << 4) // image uses struct image_trailer_v2 |
||
250 | |||
251 | |||
252 | // bitmapped flags superposed to a filesystem entry's inode number |
||
253 | #define IFS_INO_PROCESSED_ELF 0x80000000 |
||
254 | #define IFS_INO_RUNONCE_ELF 0x40000000 |
||
255 | #define IFS_INO_BOOTSTRAP_EXE 0x20000000 |
||
256 | |||
257 | |||
11 | pmbaty | 258 | // miscellaneous macros |
259 | #define ROUND_TO_UPPER_MULTIPLE(val,multiple) ((((val) + (size_t) (multiple) - 1) / (multiple)) * (multiple)) // note that val is being evaluated once, so it can be the result of a function call |
||
260 | #ifdef _WIN32 |
||
261 | #define IS_DIRSEP(c) (((c) == '/') || ((c) == '\\')) // platform-specific directory separator, Win32 variant |
||
262 | #define PATH_SEP ';' // platform-specific PATH element separator, Win32 variant |
||
263 | #define PATH_SEP_STR ";" // platform-specific PATH element separator (as string), Win32 variant |
||
264 | #else // !_WIN32, thus POSIX |
||
265 | #define IS_DIRSEP(c) ((c) == '/') // platform-specific directory separator, UNIX variant |
||
266 | #define PATH_SEP ':' // platform-specific PATH element separator, UNIX variant |
||
267 | #define PATH_SEP_STR ":" // platform-specific PATH element separator (as string), UNIX variant |
||
268 | #endif // _WIN32 |
||
269 | #define RECORD_SEP '\x1e' // arbitrarily-chosen ASCII record separator |
||
270 | #define RECORD_SEP_STR "\x1e" // arbitrarily-chosen ASCII record separator (as string) |
||
271 | |||
272 | |||
1 | pmbaty | 273 | // SHA-512 block and digest sizes |
274 | #define SHA512_BLOCK_LENGTH 128 // in bytes |
||
275 | #define SHA512_DIGEST_LENGTH 64 // in bytes |
||
276 | |||
277 | |||
278 | // SHA-512 computation context structure type definition |
||
279 | typedef struct sha512_ctx_s |
||
280 | { |
||
281 | uint64_t state[8]; |
||
282 | uint64_t bitcount[2]; |
||
283 | uint8_t buffer[SHA512_BLOCK_LENGTH]; |
||
284 | } SHA512_CTX; |
||
285 | |||
286 | |||
287 | #if 0 // TODO: startup script compiler. Someday. |
||
288 | #define SCRIPT_FLAGS_EXTSCHED 0x01 |
||
289 | #define SCRIPT_FLAGS_SESSION 0x02 |
||
290 | #define SCRIPT_FLAGS_SCHED_SET 0x04 |
||
291 | #define SCRIPT_FLAGS_CPU_SET 0x08 |
||
292 | #define SCRIPT_FLAGS_BACKGROUND 0x20 |
||
293 | #define SCRIPT_FLAGS_KDEBUG 0x40 |
||
294 | |||
295 | #define SCRIPT_POLICY_NOCHANGE 0 |
||
296 | #define SCRIPT_POLICY_FIFO 1 |
||
297 | #define SCRIPT_POLICY_RR 2 |
||
298 | #define SCRIPT_POLICY_OTHER 3 |
||
299 | |||
300 | #define SCRIPT_TYPE_EXTERNAL 0 |
||
301 | #define SCRIPT_TYPE_WAITFOR 1 |
||
302 | #define SCRIPT_TYPE_REOPEN 2 |
||
303 | #define SCRIPT_TYPE_DISPLAY_MSG 3 |
||
304 | #define SCRIPT_TYPE_PROCMGR_SYMLINK 4 |
||
305 | #define SCRIPT_TYPE_EXTSCHED_APS 5 |
||
306 | |||
307 | #define SCRIPT_CHECKS_MS 100 |
||
308 | |||
309 | #define SCRIPT_SCHED_EXT_NONE 0 |
||
310 | #define SCRIPT_SCHED_EXT_APS 1 |
||
311 | |||
312 | #define SCRIPT_APS_SYSTEM_PARTITION_ID 0 |
||
313 | #define SCRIPT_APS_SYSTEM_PARTITION_NAME "System" |
||
314 | #define SCRIPT_APS_PARTITION_NAME_LENGTH 15 |
||
315 | #define SCRIPT_APS_MAX_PARTITIONS 8 |
||
316 | |||
317 | |||
11 | pmbaty | 318 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
319 | typedef PACKED (struct) bootscriptcmd_header_s |
||
1 | pmbaty | 320 | { |
321 | uint16_t size; // size of cmd entry |
||
322 | uint8_t type; |
||
323 | uint8_t spare; |
||
324 | } bootscriptcmd_header_t; |
||
11 | pmbaty | 325 | END_OF_PACKED_STRUCT () // restore default alignment |
1 | pmbaty | 326 | |
327 | |||
11 | pmbaty | 328 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
1 | pmbaty | 329 | typedef union bootscriptcmd_s |
330 | { |
||
11 | pmbaty | 331 | PACKED (struct) script_external |
1 | pmbaty | 332 | { |
333 | bootscriptcmd_header_t hdr; |
||
334 | uint8_t cpu; // CPU (turn into runmask) |
||
335 | uint8_t flags; |
||
336 | union script_external_extsched |
||
337 | { |
||
338 | uint8_t reserved[2]; |
||
11 | pmbaty | 339 | PACKED (struct) |
1 | pmbaty | 340 | { |
341 | uint8_t id; |
||
342 | uint8_t reserved[1]; |
||
343 | } aps; |
||
344 | } extsched; // extended scheduler |
||
345 | uint8_t policy; // POLICY_FIFO, POLICY_RR, ... |
||
346 | uint8_t priority; // priority to run cmd at |
||
347 | uint8_t argc; // # of args |
||
348 | uint8_t envc; // # of environment entries |
||
349 | char args[0]; // executable, argv, envp (null padded to 32-bit align) |
||
350 | } external; |
||
11 | pmbaty | 351 | PACKED (struct) script_waitfor_reopen |
1 | pmbaty | 352 | { |
353 | bootscriptcmd_header_t hdr; |
||
354 | uint16_t checks; |
||
355 | char fname[0]; // char fname[] (null padded to 32-bit align) |
||
356 | } waitfor_reopen; |
||
11 | pmbaty | 357 | PACKED (struct) script_display_msg |
1 | pmbaty | 358 | { |
359 | bootscriptcmd_header_t hdr; |
||
360 | char msg[0]; // char msg[] (null padded to 32-bit align) |
||
361 | } display_msg; |
||
11 | pmbaty | 362 | PACKED (struct) script_procmgr_symlink |
1 | pmbaty | 363 | { |
364 | bootscriptcmd_header_t hdr; |
||
365 | char src_dest[0]; // <src_name>, '\0', <dest_name> '\0' (null padded to 32-bit align) |
||
366 | } procmgr_symlink; |
||
11 | pmbaty | 367 | PACKED (struct) script_extsched_aps |
1 | pmbaty | 368 | { |
369 | bootscriptcmd_header_t hdr; |
||
370 | uint8_t parent; |
||
371 | uint8_t budget; |
||
372 | uint16_t critical; |
||
373 | uint8_t id; |
||
374 | char pname[0]; // char pname[] (null padded to 32-bit align) |
||
375 | } extsched_aps; |
||
376 | } bootscriptcmd_t; |
||
11 | pmbaty | 377 | END_OF_PACKED_STRUCT () // restore default alignment |
1 | pmbaty | 378 | #endif // 0 |
379 | |||
380 | |||
381 | #define INITIAL_STARTUP_SCRIPT \ |
||
382 | /* procmgr_symlink /proc/boot/ldqnx-64.so.2 /usr/lib/ldqnx-64.so.2 */ \ |
||
7 | pmbaty | 383 | "\x34\x00" /*size*/ "\x04" /*type*/ "\x00" /*spare*/ "/proc/boot/ldqnx-64.so.2\0" "/usr/lib/ldqnx-64.so.2\0" \ |
1 | pmbaty | 384 | /* sh /proc/boot/startup.sh */ \ |
385 | "\x88\x00" /*size*/ "\x00" /*type*/ "\x00" /*spare*/ "\x00" /*CPU mask*/ "\x00" /*flags*/ "\x00\x00" /*reserved*/ "\x00" /*policy*/ "\x00" /*priority*/ "\02" /*argc*/ "\x02" /*envc*/ "sh\0" /*executable*/ "sh\0" "/proc/boot/startup.sh\0" /*argv*/ "PATH=/sbin:/usr/sbin:/bin:/usr/bin:/proc/boot\0" "LD_LIBRARY_PATH=/proc/boot:/lib:/lib/dll:/usr/lib\0" /*envp*/ \ |
||
386 | /* display_msg "Startup complete */ \ |
||
387 | "\x18\x00" /*size*/ "\x03" /*type*/ "\x00" /*spare*/ "Startup complete\n\0" "\x00\00" /*padding*/ \ |
||
388 | /* trailer */ \ |
||
389 | "\x00\x00\x00\x00" |
||
390 | |||
391 | |||
11 | pmbaty | 392 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
393 | typedef PACKED (struct) fsentry_s |
||
1 | pmbaty | 394 | { |
11 | pmbaty | 395 | PACKED (struct) fsentry_header_s |
1 | pmbaty | 396 | { |
397 | uint16_t size; // size of dirent |
||
398 | uint16_t extattr_offset; // if zero, no extattr data |
||
399 | uint32_t ino; // if zero, skip entry |
||
400 | uint32_t mode; // mode and perms of entry |
||
401 | uint32_t gid; |
||
402 | uint32_t uid; |
||
403 | uint32_t mtime; |
||
404 | } header; |
||
11 | pmbaty | 405 | PACKED (union) fsentry_specific_u |
1 | pmbaty | 406 | { |
11 | pmbaty | 407 | PACKED (struct) fsentry_file_s // when (mode & S_IFMT) == S_IFREG |
1 | pmbaty | 408 | { |
409 | uint32_t offset; // offset from header |
||
410 | uint32_t size; |
||
411 | char *path; // null terminated path (no leading slash) |
||
412 | char *UNSAVED_databuf; // file data blob buffer (NOT SAVED IN THE IFS) |
||
413 | } file; |
||
11 | pmbaty | 414 | PACKED (struct) fsentry_dir_s // when (mode & S_IFMT) == S_IFDIR |
1 | pmbaty | 415 | { |
416 | char *path; // null terminated path (no leading slash) |
||
417 | } dir; |
||
11 | pmbaty | 418 | PACKED (struct) fsentry_symlink_s // when (mode & S_IFMT) == S_IFLNK |
1 | pmbaty | 419 | { |
420 | uint16_t sym_offset; // offset to 'contents' from 'path' |
||
421 | uint16_t sym_size; // strlen (contents) |
||
422 | char *path; // null terminated path (no leading slash) |
||
423 | char *contents; // null terminated symlink contents |
||
424 | } symlink; |
||
11 | pmbaty | 425 | PACKED (struct) fsentry_device_s // when (mode & S_IFMT) == S_IF<CHR|BLK|FIFO|NAM|SOCK> |
1 | pmbaty | 426 | { |
427 | uint32_t dev; |
||
428 | uint32_t rdev; |
||
429 | char *path; // null terminated path (no leading slash) |
||
430 | } device; |
||
431 | } u; |
||
432 | bool UNSAVED_was_data_written; // whether this entry's data was written to the image (NOT SAVED IN THE IFS) |
||
433 | } fsentry_t; |
||
11 | pmbaty | 434 | END_OF_PACKED_STRUCT () // restore default alignment |
1 | pmbaty | 435 | |
436 | |||
11 | pmbaty | 437 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
438 | typedef PACKED (struct) startup_header_s // size 256 bytes |
||
1 | pmbaty | 439 | { |
11 | pmbaty | 440 | // I - used by the QNX IPL |
441 | // S - used by the startup program |
||
1 | pmbaty | 442 | uint8_t signature[4]; // [I ] Header signature, "\xeb\x7e\xff\x00" |
443 | uint16_t version; // [I ] Header version, i.e. 1 |
||
444 | uint8_t flags1; // [IS] Misc flags, 0x21 (= 0x20 | STARTUP_HDR_FLAGS1_VIRTUAL) |
||
445 | uint8_t flags2; // [ ] No flags defined yet (0) |
||
446 | uint16_t header_size; // [ S] sizeof(struct startup_header), i.e. 256 |
||
447 | uint16_t machine; // [IS] Machine type from elfdefinitions.h, i.e. 0x003E --> _ELF_DEFINE_EM(EM_X86_64, 62, "AMD x86-64 architecture") |
||
448 | uint32_t startup_vaddr; // [I ] Virtual Address to transfer to after IPL is done, here 0x01403008 (appears in "Entry" column for "startup.*") |
||
449 | uint32_t paddr_bias; // [ S] Value to add to physical address to get a value to put into a pointer and indirected through, here 0 (no indirections) |
||
450 | uint32_t image_paddr; // [IS] Physical address of image, here 0x01400f30 (appears in "Offset" column for "startup-header" which is the first entry/start of file) |
||
451 | uint32_t ram_paddr; // [IS] Physical address of RAM to copy image to (startup_size bytes copied), here 0x01400f30 (same as above) |
||
452 | uint32_t ram_size; // [ S] Amount of RAM used by the startup program and executables contained in the file system, here 0x00cd6128 i.e. 13 459 752 dec. which is 13 Mb. i.e. IFS file size minus 0x9eee |
||
453 | uint32_t startup_size; // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes |
||
454 | uint32_t stored_size; // [I ] Size of entire image, here 0x00cd6128 (same as ram_size) |
||
455 | uint32_t imagefs_paddr; // [IS] Set by IPL to where the imagefs is when startup runs (0) |
||
456 | uint32_t imagefs_size; // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes |
||
457 | uint16_t preboot_size; // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file)) |
||
458 | uint16_t zero0; // [ ] Zeros |
||
459 | uint32_t zero[1]; // [ ] Zeros |
||
460 | uint64_t addr_off; // [ S] Offset to add to startup_vaddr, image_paddr, ram_paddr, and imagefs_paddr members, here zero (0) |
||
461 | uint32_t info[48]; // [IS] Array of startup_info* structures (zero filled) |
||
462 | } startup_header_t; |
||
11 | pmbaty | 463 | END_OF_PACKED_STRUCT () // restore default alignment |
1 | pmbaty | 464 | |
465 | |||
11 | pmbaty | 466 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
467 | typedef PACKED (struct) startup_trailer_s |
||
1 | pmbaty | 468 | { |
469 | uint32_t cksum; // checksum from start of header to start of trailer |
||
2 | pmbaty | 470 | } startup_trailer_v1_t; |
11 | pmbaty | 471 | END_OF_PACKED_STRUCT () // restore default alignment |
1 | pmbaty | 472 | |
473 | |||
474 | // NOTE: The checksums in this trailer will only be valid prior to entering startup. |
||
475 | // Because the startup binary is executed in-place, its data segment will change once the program is running. |
||
476 | // Hence, any checksum validation would need to be done by the boot loader / IFS. |
||
11 | pmbaty | 477 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
478 | typedef PACKED (struct) startup_trailer_v2_s |
||
1 | pmbaty | 479 | { |
480 | uint8_t sha512[64]; // SHA512 from start of header to start of trailer |
||
481 | uint32_t cksum; // checksum from start of header to start of this member |
||
482 | } startup_trailer_v2_t; |
||
11 | pmbaty | 483 | END_OF_PACKED_STRUCT () // restore default alignment |
1 | pmbaty | 484 | |
485 | |||
11 | pmbaty | 486 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
487 | typedef PACKED (struct) image_header_s |
||
1 | pmbaty | 488 | { |
489 | uint8_t signature[7]; // image filesystem signature, i.e. "imagefs" |
||
490 | uint8_t flags; // endian neutral flags, 0x1c |
||
6 | pmbaty | 491 | uint32_t image_size; // size from start of header to end of trailer (here 0xca6fe0 or 13 266 912) |
492 | uint32_t hdr_dir_size; // size from start of header to last dirent (here 0x12b8 or 4792) |
||
493 | uint32_t dir_offset; // offset from start of header to start of first dirent (here 0x5c or 92) |
||
1 | pmbaty | 494 | uint32_t boot_ino[4]; // inode of files for bootstrap pgms (here 0xa0000002, 0, 0, 0) |
495 | uint32_t script_ino; // inode of file for script (here 3) |
||
496 | uint32_t chain_paddr; // offset to next filesystem signature (0) |
||
497 | uint32_t spare[10]; // zerofill |
||
498 | uint32_t mountflags; // default _MOUNT_* from sys/iomsg.h (0) |
||
499 | char mountpoint[4]; // default mountpoint for image ("/" + "\0\0\0") |
||
500 | } image_header_t; |
||
11 | pmbaty | 501 | END_OF_PACKED_STRUCT () // restore default alignment |
1 | pmbaty | 502 | |
503 | |||
11 | pmbaty | 504 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
505 | typedef PACKED (struct) image_trailer_v1_s |
||
1 | pmbaty | 506 | { |
507 | uint32_t cksum; // checksum from start of header to start of trailer |
||
2 | pmbaty | 508 | } image_trailer_v1_t; // NOTE: this is the same structure as startup_trailer_v1_t |
11 | pmbaty | 509 | END_OF_PACKED_STRUCT () // restore default alignment |
1 | pmbaty | 510 | |
511 | |||
512 | // NOTE: the checksums in this trailer will only be valid until the first non-startup bootstrap binary (e.g., startup-verifier, procnto, ...) is invoked. |
||
513 | // Because bootstrap binaries execute in-place, their data segments will change once the programs are running. |
||
514 | // Hence, any checksum validation would need to be done either by the boot loader / IFS or by the startup. |
||
11 | pmbaty | 515 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
516 | typedef PACKED (struct) image_trailer_v2_s |
||
1 | pmbaty | 517 | { |
518 | uint8_t sha512[64]; // SHA512 from start of image header to start of trailer |
||
519 | uint32_t cksum; // checksum from start of header to start of this member |
||
2 | pmbaty | 520 | } image_trailer_v2_t; // NOTE: this is the same structure as startup_trailer_v2_t |
11 | pmbaty | 521 | END_OF_PACKED_STRUCT () // restore default alignment |
1 | pmbaty | 522 | |
523 | |||
7 | pmbaty | 524 | // Executable and Linkable Format master header structure type definition |
11 | pmbaty | 525 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
526 | typedef PACKED (struct) elf_header_s |
||
7 | pmbaty | 527 | { |
11 | pmbaty | 528 | PACKED (union) |
7 | pmbaty | 529 | { |
11 | pmbaty | 530 | PACKED (struct) |
7 | pmbaty | 531 | { |
10 | pmbaty | 532 | uint8_t magic[4]; // offset 0: "\x7f" + "ELF" |
7 | pmbaty | 533 | uint8_t platform_size; // offset 4: 1 = 32-bit, 2 = 64-bit |
534 | uint8_t endianness; // offset 5: 1 = little endian, 2 = big endian |
||
535 | uint8_t header_version; // offset 6: typically 1 |
||
536 | uint8_t os_abi; // offset 7: 0 = SysV, 1 = HP/UX, 2 = NetBSD, 3 = Linux, 4 = GNU/Hurd, 6 = Solaris, 7 = AIX, 8 = IRIX, 9 = FreeBSD, 10 = Tru64, 11 = Novell, 12 = OpenBSD, 13 = OpenVMS, 14 = NonStop kernel, 15 = AROS, 16 = FenixOS, 17 = Nuxi CloudABI, 18 = OpenVOS |
||
537 | uint8_t spare[8]; // offset 8: zeroes |
||
538 | uint16_t type; // offset 16: 1 = relocatable, 2 = executable, 3 = shared, 4 = core dump |
||
539 | uint16_t instruction_set; // offset 18: 2 = Sparc, 3 = i386, 8 = MIPS, 20 = PowerPC, 40 = ARM, 42 = SuperH, 50 = IA-64, 62 = x86_64, 183 = AArch64, 243 = RISC-V |
||
540 | uint32_t elf_version; // offset 20: typically 1 |
||
541 | } elf; |
||
11 | pmbaty | 542 | PACKED (struct) // size == 52 |
7 | pmbaty | 543 | { |
10 | pmbaty | 544 | uint8_t magic[4]; // offset 0: "\x7f" + "ELF" |
7 | pmbaty | 545 | uint8_t platform_size; // offset 4: 1 = 32-bit, 2 = 64-bit |
546 | uint8_t endianness; // offset 5: 1 = little endian, 2 = big endian |
||
547 | uint8_t header_version; // offset 6: typically 1 |
||
548 | uint8_t os_abi; // offset 7: 0 = SysV, 1 = HP/UX, 2 = NetBSD, 3 = Linux, 4 = GNU/Hurd, 6 = Solaris, 7 = AIX, 8 = IRIX, 9 = FreeBSD, 10 = Tru64, 11 = Novell, 12 = OpenBSD, 13 = OpenVMS, 14 = NonStop kernel, 15 = AROS, 16 = FenixOS, 17 = Nuxi CloudABI, 18 = OpenVOS |
||
549 | uint8_t spare[8]; // offset 8: zeroes |
||
550 | uint16_t type; // offset 16: 1 = relocatable, 2 = executable, 3 = shared, 4 = core dump |
||
551 | uint16_t instruction_set; // offset 18: 2 = Sparc, 3 = i386, 8 = MIPS, 20 = PowerPC, 40 = ARM, 42 = SuperH, 50 = IA-64, 62 = x86_64, 183 = AArch64, 243 = RISC-V |
||
552 | uint32_t elf_version; // offset 20: typically 1 |
||
553 | uint32_t entrypoint_offset; // offset 24: offset to program entrypoint |
||
554 | uint32_t program_header_table_offset; // offset 28: offset to program header table |
||
555 | uint32_t section_header_table_offset; // offset 32: offset to section header table |
||
556 | uint32_t flags; // offset 36: flags (architecture-dependent, none for x86) |
||
557 | uint16_t header_size; // offset 40: size of ELF header, 52 for 32-bit ELF and 64 for 64-bit ELF -- DO NOT USE sizeof() ON THE elf_header_s STRUCT BECAUSE OF THE UNION! WRITE THE CORRECT SIZE YOURSELF! |
||
558 | uint16_t program_header_item_size; // offset 42: size of an entry in the program header table |
||
559 | uint16_t program_header_table_len; // offset 44: number of entries in the program header table |
||
560 | uint16_t section_header_item_size; // offset 46: size of an entry in the section header table |
||
561 | uint16_t section_header_table_len; // offset 48: number of entries in the section header table |
||
562 | uint16_t section_header_names_idx; // offset 50: index of the entry in the section header table that contains the section names |
||
10 | pmbaty | 563 | } elf32; // size == 52 |
11 | pmbaty | 564 | PACKED (struct) // size == 64 |
7 | pmbaty | 565 | { |
10 | pmbaty | 566 | uint8_t magic[4]; // offset 0: "\x7f" + "ELF" |
7 | pmbaty | 567 | uint8_t platform_size; // offset 4: 1 = 32-bit, 2 = 64-bit |
568 | uint8_t endianness; // offset 5: 1 = little endian, 2 = big endian |
||
569 | uint8_t header_version; // offset 6: typically 1 |
||
570 | uint8_t os_abi; // offset 7: 0 = SysV, 1 = HP/UX, 2 = NetBSD, 3 = Linux, 4 = GNU/Hurd, 6 = Solaris, 7 = AIX, 8 = IRIX, 9 = FreeBSD, 10 = Tru64, 11 = Novell, 12 = OpenBSD, 13 = OpenVMS, 14 = NonStop kernel, 15 = AROS, 16 = FenixOS, 17 = Nuxi CloudABI, 18 = OpenVOS |
||
571 | uint8_t spare[8]; // offset 8: zeroes |
||
572 | uint16_t type; // offset 16: 1 = relocatable, 2 = executable, 3 = shared, 4 = core dump |
||
573 | uint16_t instruction_set; // offset 18: 2 = Sparc, 3 = i386, 8 = MIPS, 20 = PowerPC, 40 = ARM, 42 = SuperH, 50 = IA-64, 62 = x86_64, 183 = AArch64, 243 = RISC-V |
||
574 | uint32_t elf_version; // offset 20: typically 1 |
||
575 | uint64_t entrypoint_offset; // offset 24: program entry offset |
||
576 | uint64_t program_header_table_offset; // offset 32: offset to program header table |
||
577 | uint64_t section_header_table_offset; // offset 40: offset to section header table |
||
578 | uint32_t flags; // offset 48: flags (architecture-dependent, none for x86) |
||
579 | uint16_t header_size; // offset 52: size of ELF header, 52 for 32-bit ELF and 64 for 64-bit ELF |
||
580 | uint16_t program_header_item_size; // offset 54: size of an entry in the program header table |
||
581 | uint16_t program_header_table_len; // offset 56: number of entries in the program header table |
||
582 | uint16_t section_header_item_size; // offset 58: size of an entry in the section header table |
||
583 | uint16_t section_header_table_len; // offset 60: number of entries in the section header table |
||
584 | uint16_t section_header_names_idx; // offset 62: index of the entry in the section header table that contains the section names |
||
10 | pmbaty | 585 | } elf64; // size == 64 |
7 | pmbaty | 586 | } u; |
587 | } elf_header_t; |
||
11 | pmbaty | 588 | END_OF_PACKED_STRUCT () // restore default alignment |
7 | pmbaty | 589 | |
590 | |||
10 | pmbaty | 591 | // Executable and Linkable Format program header structure type definition |
11 | pmbaty | 592 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
593 | typedef PACKED (struct) elf_program_header_s |
||
10 | pmbaty | 594 | { |
11 | pmbaty | 595 | PACKED (union) |
10 | pmbaty | 596 | { |
11 | pmbaty | 597 | PACKED (struct) |
10 | pmbaty | 598 | { |
599 | uint32_t segment_type; // offset 0: type of segment (0: unused table entry, 1: loadable, 2: dynamic linking information, 3: interpreter information, 4: auxiliary information, 5: reserved, 6: this very segment, 7: TLS template) |
||
600 | } elf; |
||
11 | pmbaty | 601 | PACKED (struct) // size == 32 |
10 | pmbaty | 602 | { |
603 | uint32_t segment_type; // offset 0: type of segment (0: unused table entry, 1: loadable, 2: dynamic linking information, 3: interpreter information, 4: auxiliary information, 5: reserved, 6: this very segment, 7: TLS template) |
||
604 | uint32_t file_offset; // offset 4: file offset of this segment |
||
605 | uint32_t virtual_addr; // offset 8: virtual address where this segment should be mapped in memory |
||
606 | uint32_t physical_addr; // offset 12: on systems where this is relevant, PHYSICAL address where this segment should be mapped in memory |
||
607 | uint32_t size_in_file; // offset 16: size of this segment in the ELF file (may be zero) |
||
608 | uint32_t size_in_memory; // offset 20: size of this segment in memory (may be zero) |
||
609 | uint32_t segment_flags; // offset 24: bitmap of segment flags (1: executable, 2: writable, 4: readable) |
||
610 | uint32_t alignment; // offset 28: memory alignment (0 or 1 mean non alignment, else must be a power of 2 where virtual_addr == file_offset % alignment) |
||
611 | } elf32; // size == 32 |
||
11 | pmbaty | 612 | PACKED (struct) // size == 56 |
10 | pmbaty | 613 | { |
614 | uint32_t segment_type; // offset 0: type of segment (0: unused table entry, 1: loadable, 2: dynamic linking information, 3: interpreter information, 4: auxiliary information, 5: reserved, 6: this very segment, 7: TLS template) |
||
615 | uint32_t segment_flags; // offset 4: bitmap of segment flags (1: executable, 2: writable, 4: readable) |
||
616 | uint64_t file_offset; // offset 8: file offset of this segment |
||
617 | uint64_t virtual_addr; // offset 16: virtual address where this segment should be mapped in memory |
||
618 | uint64_t physical_addr; // offset 24: on systems where this is relevant, PHYSICAL address where this segment should be mapped in memory |
||
619 | uint64_t size_in_file; // offset 32: size of this segment in the ELF file (may be zero) |
||
620 | uint64_t size_in_memory; // offset 40: size of this segment in memory (may be zero) |
||
621 | uint64_t alignment; // offset 48: memory alignment (0 or 1 mean non alignment, else must be a power of 2 where virtual_addr == file_offset % alignment) |
||
622 | } elf64; // size == 56 |
||
623 | } u; |
||
624 | } elf_program_header_t; |
||
11 | pmbaty | 625 | END_OF_PACKED_STRUCT () // restore default alignment |
10 | pmbaty | 626 | |
627 | |||
7 | pmbaty | 628 | // Executable and Linkable Format section header structure type definition |
11 | pmbaty | 629 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
630 | typedef PACKED (struct) elf_section_header_s |
||
7 | pmbaty | 631 | { |
11 | pmbaty | 632 | PACKED (union) |
7 | pmbaty | 633 | { |
11 | pmbaty | 634 | PACKED (struct) |
7 | pmbaty | 635 | { |
11 | pmbaty | 636 | uint32_t name_offset; // offset 0: offset in the string table of the name of this section |
637 | uint32_t type; // offset 4: section type (0: unused, 1: program data, 2: symbols table, 3: strings table, 4: relocs with addends, 5: symbols hash table, 6: dyld info, 7: notes, 8: BSS, 9: relocs without addends, 11: dyld symbols table, 14: constructors, 15: destructors, 16, preconstructors, 17: group, 18: extended section indices, 19: number of typedefs ...) |
||
638 | } elf; |
||
639 | PACKED (struct) // size == 40 |
||
640 | { |
||
7 | pmbaty | 641 | uint32_t name_offset; // offset 0: offset in the string table of the name of this section |
11 | pmbaty | 642 | uint32_t type; // offset 4: section type (0: unused, 1: program data, 2: symbols table, 3: strings table, 4: relocs with addends, 5: symbols hash table, 6: dyld info, 7: notes, 8: BSS, 9: relocs without addends, 11: dyld symbols table, 14: constructors, 15: destructors, 16, preconstructors, 17: group, 18: extended section indices, 19: number of typedefs ...) |
643 | uint32_t flags; // offset 8: bitmapped flags (1: writable, 2: takes RAM, 4: executable, 8: reserved, 16: mergeable, 32: contains C-strings, 64: sh_info contains SHT index, 128: preserve order, 256: OS-specific, 512: group member, 1024: TLS template ...) |
||
7 | pmbaty | 644 | uint32_t virtual_addr; // offset 12: address in virtual memory where this section may be loaded |
645 | uint32_t file_offset; // offset 16: offset of this section in the ELF file |
||
646 | uint32_t size; // offset 20: size of this section |
||
647 | uint32_t linked_index; // offset 24: optional section index of an associated section |
||
648 | uint32_t info; // offset 28: optional extra information |
||
649 | uint32_t alignment; // offset 32: required memory alignment (must be a power of 2) |
||
650 | uint32_t entry_size; // offset 36: for table-like sections, size of an element in the table |
||
10 | pmbaty | 651 | } elf32; // size == 40 |
11 | pmbaty | 652 | PACKED (struct) // size == 64 |
7 | pmbaty | 653 | { |
654 | uint32_t name_offset; // offset 0: offset in the string table of the name of this section |
||
11 | pmbaty | 655 | uint32_t type; // offset 4: section type (0: unused, 1: program data, 2: symbols table, 3: strings table, 4: relocs with addends, 5: symbols hash table, 6: dyld info, 7: notes, 8: BSS, 9: relocs without addends, 11: dyld symbols table, 14: constructors, 15: destructors, 16, preconstructors, 17: group, 18: extended section indices, 19: number of typedefs ...) |
656 | uint64_t flags; // offset 8: bitmapped flags (1: writable, 2: takes RAM, 4: executable, 8: reserved, 16: mergeable, 32: contains C-strings, 64: sh_info contains SHT index, 128: preserve order, 256: OS-specific, 512: group member, 1024: TLS template ...) |
||
7 | pmbaty | 657 | uint64_t virtual_addr; // offset 16: address in virtual memory where this section may be loaded |
658 | uint64_t file_offset; // offset 24: offset of this section in the ELF file |
||
659 | uint64_t size; // offset 32: size of this section |
||
660 | uint32_t linked_index; // offset 40: optional section index of an associated section |
||
661 | uint32_t info; // offset 44: optional extra information |
||
662 | uint64_t alignment; // offset 48: required memory alignment (must be a power of 2) |
||
663 | uint64_t entry_size; // offset 56: for table-like sections, size of an element in the table |
||
10 | pmbaty | 664 | } elf64; // size == 64 |
7 | pmbaty | 665 | } u; |
666 | } elf_section_header_t; |
||
11 | pmbaty | 667 | END_OF_PACKED_STRUCT () // restore default alignment |
7 | pmbaty | 668 | |
669 | |||
670 | // Executable and Linkable Format dynamic section entry structure type definition |
||
11 | pmbaty | 671 | START_OF_PACKED_STRUCT () // we need byte-alignment for this struct |
672 | typedef PACKED (struct) elf_dynamic_section_entry_s |
||
7 | pmbaty | 673 | { |
11 | pmbaty | 674 | PACKED (union) |
7 | pmbaty | 675 | { |
11 | pmbaty | 676 | PACKED (struct) // size == 8 |
7 | pmbaty | 677 | { |
678 | int32_t tag; // dynamic entry type (one of ELF_DT_xxx #defines) |
||
11 | pmbaty | 679 | uint32_t value; // value (as integer, or as pointed address) |
10 | pmbaty | 680 | } elf32; // size == 8 |
11 | pmbaty | 681 | PACKED (struct) // size == 16 |
7 | pmbaty | 682 | { |
683 | int64_t tag; // dynamic entry type (one of ELF_DT_xxx #defines) |
||
11 | pmbaty | 684 | uint64_t value; // value (as integer, or as pointed address) |
10 | pmbaty | 685 | } elf64; // size == 16 |
7 | pmbaty | 686 | } u; |
687 | } elf_dynamic_section_entry_t; |
||
11 | pmbaty | 688 | END_OF_PACKED_STRUCT () // restore default alignment |
7 | pmbaty | 689 | |
690 | |||
11 | pmbaty | 691 | // generic buffer structure type definition |
692 | typedef struct buffer_s |
||
693 | { |
||
694 | uint8_t *bytes; // mallocated data |
||
695 | size_t len; // length of allocated data |
||
696 | } buffer_t; |
||
1 | pmbaty | 697 | |
698 | |||
11 | pmbaty | 699 | // IFS directory entry insertion parameters structure type definition |
1 | pmbaty | 700 | typedef struct parms_s |
701 | { |
||
702 | int dperms; // directory permissions (e.g. 0755) |
||
703 | int perms; // file permissions (e.g. 0644) |
||
704 | int uid; // owner user ID (e.g. 0 = root) |
||
705 | int gid; // owner group ID (e.g. 0 = root) |
||
706 | int st_mode; // entry type (e.g. S_IFREG for files) and permissions |
||
7 | pmbaty | 707 | uint32_t mtime; // entry's modification time POSIX timestamp - set to UINT32_MAX to use the concerned files' mtime on the build host |
708 | uint32_t mtime_for_inline_files; // same as above but only for files that don't exist on the build host (i.e. files with an explicit content blob) |
||
1 | pmbaty | 709 | char prefix[MAXPATHLEN]; // install path (e.g. "proc/boot") |
7 | pmbaty | 710 | bool should_follow_symlinks; // follow symlinks |
711 | bool should_autosymlink_dylib; // dynamic libraries should be written under their official SONAME and a named symlink be created pointing at them |
||
15 | pmbaty | 712 | bool should_keep_ld_output; // whether to keep .sym files produced by ld calls, togglable by the [+keeplinked] attribute |
2 | pmbaty | 713 | bool is_compiled_bootscript; // entry has [+script] attribute |
10 | pmbaty | 714 | int extra_ino_flags; // bitmap of extra inode flags (IFS_INO_xxx) |
1 | pmbaty | 715 | char search[10 * MAXPATHLEN]; // binary search path (the default one will be constructed at startup) |
2 | pmbaty | 716 | |
11 | pmbaty | 717 | buffer_t data; |
1 | pmbaty | 718 | } parms_t; |
719 | |||
720 | |||
721 | // global variables |
||
722 | static char line_buffer[4096]; // scrap buffer for the IFS build file parser |
||
723 | static uint32_t image_base = 4 * 1024 * 1024; // default image base, as per QNX docs -- can be changed with the [image=XXXX] attribute in the IFS build file |
||
724 | static uint32_t image_end = UINT32_MAX; // default image end (no limit) |
||
725 | static uint32_t image_maxsize = UINT32_MAX; // default image max size (no limit) |
||
726 | static uint32_t image_totalsize = 0; // image total size, measured once all the blocks have been written to the output IFS file |
||
727 | static uint32_t image_align = 4; // default image alignment, as per QNX docs |
||
728 | static uint32_t image_kernel_ino = 0; |
||
729 | static uint32_t image_bootscript_ino = 0; |
||
7 | pmbaty | 730 | #if defined(__x86_64__) |
1 | pmbaty | 731 | static char image_processor[16] = "x86_64"; // default CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET) |
7 | pmbaty | 732 | #elif defined(__aarch64__) |
733 | static char image_processor[16] = "aarch64le"; // default CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET) |
||
734 | #else // unknown platform |
||
735 | #error Please port ifstool to this platform |
||
736 | #endif |
||
15 | pmbaty | 737 | static int verbose_level = 1; // verbosity level, can be increased with multiple -v[...] flags |
1 | pmbaty | 738 | static char *buildfile_pathname = NULL; // pathname of IFS build file |
7 | pmbaty | 739 | static char *current_line = NULL; // copy of current line in IFS build file |
1 | pmbaty | 740 | static int lineno = 0; // current line number in IFS build file |
741 | static char *QNX_TARGET = NULL; // value of the $QNX_TARGET environment variable |
||
7 | pmbaty | 742 | static char *MKIFS_PATH = NULL; // value of the $MKIFS_PATH environment variable (may contain references to $QNX_TARGET). Initialized by this program if empty. |
1 | pmbaty | 743 | |
15 | pmbaty | 744 | // bootable IFS support |
745 | static char *bootfile_pathname = NULL; // HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS |
||
746 | static size_t bootfile_size = 0; // HACK: size of the bootcode binary blob file to put at the start of a bootable IFS |
||
747 | static char *startupfile_pathname = NULL; // HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS |
||
748 | static size_t startupfile_ep_from_imagebase = 0; // HACK: startup code entrypoint offset from image base for a bootable IFS |
||
749 | static char *kernelfile_pathname = NULL; // HACK: pathname to precompiled kernel file blob to put in a bootable IFS |
||
750 | static size_t kernelfile_offset = 0; // HACK: kernel file offset in bootable IFS |
||
1 | pmbaty | 751 | |
15 | pmbaty | 752 | |
1 | pmbaty | 753 | // prototypes of local functions |
754 | static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data); // used internally in SHA512_Update() and SHA512_Final() |
||
755 | static void SHA512_Init (SHA512_CTX *context); |
||
756 | static void SHA512_Update (SHA512_CTX *context, void *data, size_t len); |
||
757 | static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context); |
||
758 | static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest); // computes a SHA-512 in one pass (shortcut for SHA512_Init(), SHA512_Update() N times and SHA512_Final()) |
||
7 | pmbaty | 759 | static int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness); // compute an IFS image or startup checksum to store in the trailer |
1 | pmbaty | 760 | static long long read_integer (const char *str); // reads an integer number for a string that may be specified in either hex, octal or decimal base, and may have an optional unit suffix (k, m, g, t) |
2 | pmbaty | 761 | static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...); // hexdump-style formatted output to a file stream (which may be stdout/stderr) |
762 | static char *binary (const uint8_t x, char char_for_zero, char char_for_one); // returns the binary representation of byte 'x' as a string |
||
763 | static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8]); // returns the ORed description of byte 'x' according to the description strings for each bit |
||
11 | pmbaty | 764 | static char *read_filecontents (const char *pathname, const char *search_path, buffer_t *outbuf); // locates pathname among MKIFS_PATH, reads it, places its contents in a buffer (caller frees) and returns a pointer to the resolved pathname (static string) |
1 | pmbaty | 765 | static int fwrite_filecontents (const char *pathname, FILE *fp); // dumps the contents of pathname into fp |
11 | pmbaty | 766 | static int relative_offset_of_in (const char *name, const buffer_t *stringbuf); // returns the relative offset of a particular string in a string table |
10 | pmbaty | 767 | static elf_section_header_t *elf_get_section_header_by_name (const elf_header_t *elf, const char *section_name); // get a pointer to a named section header in an ELF file |
1 | pmbaty | 768 | static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp); // writes the given filesystem entry into fp (without its contents) |
7 | pmbaty | 769 | static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, parms_t *entry_parms, const char *stored_pathname, const char *buildhost_pathname); // stack up a new filesystem entry |
1 | pmbaty | 770 | static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames |
7 | pmbaty | 771 | static void update_MKIFS_PATH (const char *processor); |
10 | pmbaty | 772 | static int dump_ifs_info (const char *ifs_pathname, bool want_everything); // dumps detailed info about a particular IFS file on the standard output, returns 0 on success and >0 on error |
14 | pmbaty | 773 | static int create_intermediate_dirs (const char *file_pathname); // creates all intermediate directories to file_pathname so that fopen(file_pathname, "w") doesn't fail |
774 | static int dump_ifs_contents (const char *ifs_pathname, const char *outdir); // dumps the IFS filesystem contents in outdir, returns 0 on success and >0 on error |
||
1 | pmbaty | 775 | |
776 | |||
777 | static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data) |
||
778 | { |
||
779 | // logical functions used in SHA-384 and SHA-512 |
||
780 | #define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) // 64-bit rotate right |
||
781 | #define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) |
||
782 | #define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) |
||
783 | #define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) |
||
784 | #define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) |
||
785 | #define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ ((x) >> 7)) |
||
786 | #define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ ((x) >> 6)) |
||
787 | |||
788 | // hash constant words K for SHA-384 and SHA-512 |
||
789 | static const uint64_t K512[80] = { |
||
790 | 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, |
||
791 | 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, |
||
792 | 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, |
||
793 | 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, |
||
794 | 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, |
||
795 | 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, |
||
796 | 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, |
||
797 | 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, |
||
798 | 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, |
||
799 | 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL |
||
800 | }; |
||
801 | |||
802 | uint64_t a, b, c, d, e, f, g, h, s0, s1; |
||
803 | uint64_t T1, T2, *W512 = (uint64_t *) context->buffer; |
||
804 | int j; |
||
805 | |||
806 | // initialize registers with the prev. intermediate value |
||
807 | a = context->state[0]; b = context->state[1]; c = context->state[2]; d = context->state[3]; e = context->state[4]; f = context->state[5]; g = context->state[6]; h = context->state[7]; |
||
808 | |||
809 | for (j = 0; j < 16; j++) |
||
810 | { |
||
811 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
||
812 | W512[j] = __builtin_bswap64 (*data); // convert to host byte order |
||
813 | #elif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
||
814 | W512[j] = *data; |
||
815 | #else // __BYTE_ORDER__ == ??? |
||
816 | #error Please port this SHA-512 code to your exotic endianness platform. What are you compiling this on? PDP? Honeywell? |
||
817 | #endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
||
818 | |||
819 | // apply the SHA-512 compression function to update a..h |
||
820 | T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + W512[j]; |
||
821 | T2 = Sigma0_512 (a) + Maj (a, b, c); |
||
822 | |||
823 | // update registers |
||
824 | h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; |
||
825 | |||
826 | data++; |
||
827 | } |
||
828 | |||
829 | for (; j < 80; j++) |
||
830 | { |
||
831 | // part of the message block expansion |
||
832 | s0 = W512[(j + 1) & 0x0f]; |
||
833 | s0 = sigma0_512 (s0); |
||
834 | s1 = W512[(j + 14) & 0x0f]; |
||
835 | s1 = sigma1_512 (s1); |
||
836 | |||
837 | // apply the SHA-512 compression function to update a..h |
||
838 | T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0); |
||
839 | T2 = Sigma0_512 (a) + Maj (a, b, c); |
||
840 | |||
841 | // update registers |
||
842 | h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; |
||
843 | } |
||
844 | |||
845 | // compute the current intermediate hash value |
||
846 | context->state[0] += a; context->state[1] += b; context->state[2] += c; context->state[3] += d; context->state[4] += e; context->state[5] += f; context->state[6] += g; context->state[7] += h; |
||
847 | |||
848 | // clean up |
||
849 | a = b = c = d = e = f = g = h = T1 = T2 = 0; |
||
850 | #undef sigma1_512 |
||
851 | #undef sigma0_512 |
||
852 | #undef Sigma1_512 |
||
853 | #undef Sigma0_512 |
||
854 | #undef Maj |
||
855 | #undef Ch |
||
856 | #undef S64 |
||
857 | return; |
||
858 | } |
||
859 | |||
860 | |||
861 | static void SHA512_Init (SHA512_CTX *context) |
||
862 | { |
||
863 | // initial hash value H for SHA-512 |
||
864 | static const uint64_t sha512_initial_hash_value[8] = { |
||
865 | 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL |
||
866 | }; |
||
867 | |||
868 | memcpy (context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH); |
||
869 | memset (context->buffer, 0, SHA512_BLOCK_LENGTH); |
||
870 | context->bitcount[0] = context->bitcount[1] = 0; |
||
871 | } |
||
872 | |||
873 | |||
874 | void SHA512_Update (SHA512_CTX *context, void *datain, size_t len) |
||
875 | { |
||
876 | #define ADDINC128(w,n) do { \ |
||
877 | (w)[0] += (uint64_t) (n); \ |
||
878 | if ((w)[0] < (n)) \ |
||
879 | (w)[1]++; \ |
||
880 | } while (0) // macro for incrementally adding the unsigned 64-bit integer n to the unsigned 128-bit integer (represented using a two-element array of 64-bit words |
||
881 | |||
882 | size_t freespace, usedspace; |
||
883 | const uint8_t *data = (const uint8_t *) datain; |
||
884 | |||
885 | if (len == 0) |
||
886 | return; // calling with empty data is valid - we do nothing |
||
887 | |||
888 | usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; |
||
889 | if (usedspace > 0) |
||
890 | { |
||
891 | // calculate how much free space is available in the buffer |
||
892 | freespace = SHA512_BLOCK_LENGTH - usedspace; |
||
893 | |||
894 | if (len >= freespace) |
||
895 | { |
||
896 | // fill the buffer completely and process it |
||
897 | memcpy (&context->buffer[usedspace], data, freespace); |
||
898 | ADDINC128 (context->bitcount, freespace << 3); |
||
899 | len -= freespace; |
||
900 | data += freespace; |
||
901 | sha512_private_transform (context, (uint64_t *) context->buffer); |
||
902 | } |
||
903 | else |
||
904 | { |
||
905 | // the buffer is not full yet |
||
906 | memcpy (&context->buffer[usedspace], data, len); |
||
907 | ADDINC128 (context->bitcount, len << 3); |
||
908 | |||
909 | // clean up |
||
910 | usedspace = freespace = 0; |
||
911 | return; |
||
912 | } |
||
913 | } |
||
914 | |||
915 | while (len >= SHA512_BLOCK_LENGTH) |
||
916 | { |
||
917 | // process as many complete blocks as we can |
||
918 | sha512_private_transform (context, (uint64_t *) data); |
||
919 | ADDINC128 (context->bitcount, SHA512_BLOCK_LENGTH << 3); |
||
920 | len -= SHA512_BLOCK_LENGTH; |
||
921 | data += SHA512_BLOCK_LENGTH; |
||
922 | } |
||
923 | |||
924 | if (len > 0) |
||
925 | { |
||
926 | // save leftovers |
||
927 | memcpy (context->buffer, data, len); |
||
928 | ADDINC128 (context->bitcount, len << 3); |
||
929 | } |
||
930 | |||
931 | // clean up |
||
932 | usedspace = freespace = 0; |
||
933 | #undef ADDINC128 |
||
934 | return; |
||
935 | } |
||
936 | |||
937 | |||
938 | static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context) |
||
939 | { |
||
940 | #define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) |
||
941 | |||
942 | size_t usedspace; |
||
943 | union { uint8_t *as_bytes; uint64_t *as_uint64s; } cast_var = { NULL }; |
||
944 | |||
945 | // if no digest buffer is passed, don't bother finalizing the computation |
||
946 | if (digest != NULL) |
||
947 | { |
||
948 | usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; |
||
949 | |||
950 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
||
951 | context->bitcount[0] = __builtin_bswap64 (context->bitcount[0]); // convert from host byte order |
||
952 | context->bitcount[1] = __builtin_bswap64 (context->bitcount[1]); // convert from host byte order |
||
953 | #endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
||
954 | |||
955 | if (usedspace > 0) |
||
956 | { |
||
957 | // begin padding with a 1 bit |
||
958 | context->buffer[usedspace++] = 0x80; |
||
959 | |||
960 | if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) |
||
961 | memset (&context->buffer[usedspace], 0, SHA512_SHORT_BLOCK_LENGTH - usedspace); // set-up for the last transform |
||
962 | else |
||
963 | { |
||
964 | if (usedspace < SHA512_BLOCK_LENGTH) |
||
965 | memset (&context->buffer[usedspace], 0, SHA512_BLOCK_LENGTH - usedspace); |
||
966 | |||
967 | sha512_private_transform (context, (uint64_t *) context->buffer); // do second-to-last transform |
||
968 | memset (context->buffer, 0, SHA512_BLOCK_LENGTH - 2); // and set-up for the last transform |
||
969 | } |
||
970 | } |
||
971 | else // usedspace == 0 |
||
972 | { |
||
973 | memset (context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH); // prepare for final transform |
||
974 | *context->buffer = 0x80; // begin padding with a 1 bit |
||
975 | } |
||
976 | |||
977 | // store the length of input data (in bits) |
||
978 | cast_var.as_bytes = context->buffer; |
||
979 | cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 0] = context->bitcount[1]; |
||
980 | cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 1] = context->bitcount[0]; |
||
981 | |||
982 | // final transform |
||
983 | sha512_private_transform (context, (uint64_t *) context->buffer); |
||
984 | |||
985 | // save the hash data for output |
||
986 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
||
987 | for (int j = 0; j < 8; j++) |
||
988 | context->state[j] = __builtin_bswap64 (context->state[j]); // convert to host byte order |
||
989 | #endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
||
990 | memcpy (digest, context->state, SHA512_DIGEST_LENGTH); |
||
991 | } |
||
992 | |||
993 | // zero out state data |
||
994 | memset (context, 0, sizeof (SHA512_CTX)); |
||
995 | #undef SHA512_SHORT_BLOCK_LENGTH |
||
996 | return; |
||
997 | } |
||
998 | |||
999 | |||
2 | pmbaty | 1000 | static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest_or_NULL) |
1 | pmbaty | 1001 | { |
2 | pmbaty | 1002 | // computes the SHA-512 hash of a block of data in one pass and write it to digest, or to a static buffer if NULL |
1003 | // returns the STRING REPRESENTATION of digest in a statically-allocated string |
||
1 | pmbaty | 1004 | |
8 | pmbaty | 1005 | static thread_local uint8_t static_digest[SHA512_DIGEST_LENGTH] = ""; |
1006 | static thread_local char digest_as_string[2 * SHA512_DIGEST_LENGTH + 1] = ""; |
||
2 | pmbaty | 1007 | |
1 | pmbaty | 1008 | SHA512_CTX ctx; |
2 | pmbaty | 1009 | size_t byte_index; |
1010 | |||
1 | pmbaty | 1011 | SHA512_Init (&ctx); |
1012 | SHA512_Update (&ctx, data, data_len); |
||
2 | pmbaty | 1013 | if (digest_or_NULL == NULL) |
1014 | digest_or_NULL = static_digest; |
||
1015 | SHA512_Final (digest_or_NULL, &ctx); |
||
1016 | |||
1017 | for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++) |
||
1018 | sprintf (&digest_as_string[2 * byte_index], "%02x", digest_or_NULL[byte_index]); |
||
1019 | return (digest_as_string); |
||
1 | pmbaty | 1020 | } |
1021 | |||
1022 | |||
7 | pmbaty | 1023 | static int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness) |
1 | pmbaty | 1024 | { |
6 | pmbaty | 1025 | // computes the checksum of an IFS image or startup section, i.e. from the start of the header to the end of the trailer minus the last 4 bytes where the checksum is stored |
1 | pmbaty | 1026 | |
5 | pmbaty | 1027 | uint8_t accumulator[4] = { 0, 0, 0, 0 }; |
7 | pmbaty | 1028 | const char *current_char_ptr; |
1029 | int32_t image_cksum; |
||
5 | pmbaty | 1030 | size_t i; |
1031 | |||
7 | pmbaty | 1032 | image_cksum = 0; |
1033 | current_char_ptr = data; |
||
5 | pmbaty | 1034 | for (i = 0; i < data_len; i++) |
1035 | { |
||
1036 | accumulator[i % 4] = *current_char_ptr; |
||
1037 | if (i % 4 == 3) |
||
1038 | if (is_foreign_endianness) |
||
8 | pmbaty | 1039 | image_cksum += (accumulator[3] << 0) + (accumulator[2] << 8) + (accumulator[1] << 16) + (accumulator[0] << 24); |
5 | pmbaty | 1040 | else |
8 | pmbaty | 1041 | image_cksum += (accumulator[0] << 0) + (accumulator[1] << 8) + (accumulator[2] << 16) + (accumulator[3] << 24); |
5 | pmbaty | 1042 | current_char_ptr++; |
1043 | } |
||
1044 | |||
1045 | return (is_foreign_endianness ? __builtin_bswap32 (-image_cksum) : -image_cksum); |
||
1046 | } |
||
1047 | |||
1048 | |||
1 | pmbaty | 1049 | static long long read_integer (const char *str) |
1050 | { |
||
1051 | // reads a number for a string that may be specified in either hex, octal or decimal base, and may have an optional unit suffix (k, m, g, t) |
||
1052 | |||
1053 | char *endptr = NULL; |
||
1054 | long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases |
||
1055 | if (endptr != NULL) |
||
1056 | { |
||
15 | pmbaty | 1057 | if ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024; |
1 | pmbaty | 1058 | else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024; |
1059 | else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024; |
||
1060 | else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose? |
||
1061 | } |
||
1062 | return (ret); |
||
1063 | } |
||
1064 | |||
1065 | |||
2 | pmbaty | 1066 | static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...) |
1067 | { |
||
1068 | // this function logs hexadecimal data to an opened file pointer (or to stdout/stderr) |
||
1069 | |||
1070 | va_list argptr; |
||
1071 | size_t index; |
||
1072 | int i; |
||
1073 | |||
1074 | // concatenate all the arguments in one string and write it to the file |
||
1075 | va_start (argptr, fmt); |
||
1076 | vfprintf (fp, fmt, argptr); |
||
1077 | va_end (argptr); |
||
1078 | |||
1079 | // for each row of howmany_columns bytes of data... |
||
1080 | for (index = 0; index < data_size; index += howmany_columns) |
||
1081 | { |
||
1082 | fprintf (fp, " %05zu ", index); // print array address of row |
||
1083 | for (i = 0; i < howmany_columns; i++) |
||
1084 | if (index + i < data_size) |
||
1085 | fprintf (fp, " %02X", data[index + i]); // if row contains data, print data as hex bytes |
||
1086 | else |
||
1087 | fprintf (fp, " "); // else fill the space with blanks |
||
1088 | fprintf (fp, " "); |
||
1089 | for (i = 0; i < howmany_columns; i++) |
||
1090 | if (index + i < data_size) |
||
1091 | fputc ((data[index + i] >= 32) && (data[index + i] < 127) ? data[index + i] : '.', fp); // now if row contains data, print data as ASCII |
||
1092 | else |
||
1093 | fputc (' ', fp); // else fill the space with blanks |
||
1094 | fputc ('\n', fp); |
||
1095 | } |
||
1096 | |||
1097 | return; // and return |
||
1098 | } |
||
1099 | |||
1100 | |||
1101 | static char *binary (const uint8_t x, char char_for_zero, char char_for_one) |
||
1102 | { |
||
1103 | // returns the binary representation of x as a string |
||
1104 | |||
8 | pmbaty | 1105 | static thread_local char outstr[9] = "00000000"; |
2 | pmbaty | 1106 | for (int i = 0; i < 8; i++) |
1107 | outstr[i] = (x & (0x80 >> i) ? char_for_one : char_for_zero); |
||
1108 | return (outstr); |
||
1109 | } |
||
1110 | |||
1111 | |||
1112 | static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8]) |
||
1113 | { |
||
1114 | // returns the ORed description of byte 'x' according to the description strings for each bit |
||
1115 | |||
8 | pmbaty | 1116 | static thread_local char *default_bitstrings[8] = { "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7" }; |
1117 | static thread_local char outstr[8 * 64] = ""; |
||
2 | pmbaty | 1118 | |
1119 | outstr[0] = 0; |
||
1120 | for (int i = 0; i < 8; i++) |
||
1121 | if (x & (1 << i)) |
||
1122 | { |
||
1123 | if (outstr[0] != 0) |
||
1124 | strcat (outstr, "|"); |
||
1125 | strcat (outstr, ((bitwise_stringdescs != NULL) && (*bitwise_stringdescs[i] != 0) ? bitwise_stringdescs[i] : default_bitstrings[i])); |
||
1126 | } |
||
1127 | return (outstr); |
||
1128 | } |
||
1129 | |||
1130 | |||
11 | pmbaty | 1131 | static char *read_filecontents (const char *pathname, const char *search_path, buffer_t *outbuf) |
7 | pmbaty | 1132 | { |
1133 | // locates pathname among MKIFS_PATH, and places its contents in a buffer (caller frees). Returns resolved pathname (static buffer) or NULL. |
||
1134 | |||
15 | pmbaty | 1135 | static thread_local char resolved_pathname[MAXPATHLEN]; |
7 | pmbaty | 1136 | |
1137 | const char *nextsep; |
||
1138 | const char *token; |
||
1139 | FILE *fp; |
||
1140 | |||
1141 | // is it an absolute pathname (POSIX and Windows variants) ? |
||
1142 | if (IS_DIRSEP (pathname[0]) || (isalpha (pathname[0]) && (pathname[1] == ':') && IS_DIRSEP (pathname[2]))) |
||
15 | pmbaty | 1143 | strcpy (resolved_pathname, pathname); // in this case, it MUST exist at its designated location (either absolute or relative to the current working directory) |
7 | pmbaty | 1144 | else // the path is relative, search it among the search paths we have |
1145 | { |
||
1146 | // construct a potential final path using each element of the search path |
||
8 | pmbaty | 1147 | token = (*search_path != 0 ? search_path : NULL); |
1148 | nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP_STR)] : NULL); |
||
7 | pmbaty | 1149 | while (token != NULL) |
1150 | { |
||
15 | pmbaty | 1151 | sprintf (resolved_pathname, "%.*s/%s", (int) (nextsep - token), token, pathname); |
1152 | if (access (resolved_pathname, 0) == 0) |
||
7 | pmbaty | 1153 | break; // if a file can indeed be found at this location, stop searching |
1154 | |||
1155 | token = (*nextsep != 0 ? nextsep + 1 : NULL); |
||
1156 | nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP_STR)] : NULL); |
||
1157 | } |
||
1158 | |||
1159 | // have we exhausted all possibilities ? |
||
1160 | if (token == NULL) |
||
1161 | { |
||
1162 | errno = ENOENT; |
||
1163 | return (NULL); // file not found, return with ENOENT |
||
1164 | } |
||
1165 | } |
||
1166 | |||
1167 | // now open and read the file |
||
15 | pmbaty | 1168 | fp = fopen (resolved_pathname, "rb"); |
7 | pmbaty | 1169 | if (fp == NULL) |
1170 | return (NULL); // unexistent file (errno is set to ENOENT) |
||
1171 | |||
15 | pmbaty | 1172 | // if we don't want its contents, close it and return the resolved pathname |
1173 | if (outbuf == NULL) |
||
1174 | { |
||
1175 | fclose (fp); |
||
1176 | return (resolved_pathname); |
||
1177 | } |
||
1178 | |||
1179 | // the user supplied a data buffer: read the file contents |
||
7 | pmbaty | 1180 | fseek (fp, 0, SEEK_END); |
11 | pmbaty | 1181 | outbuf->len = ftell (fp); // measure file length |
7 | pmbaty | 1182 | fseek (fp, 0, SEEK_SET); |
11 | pmbaty | 1183 | outbuf->bytes = malloc (outbuf->len); |
1184 | if (outbuf->bytes == NULL) |
||
7 | pmbaty | 1185 | { |
1186 | fclose (fp); |
||
11 | pmbaty | 1187 | outbuf->len = 0; |
7 | pmbaty | 1188 | return (NULL); // out of memory (errno is set to ENOMEM) |
1189 | } |
||
11 | pmbaty | 1190 | if (fread (outbuf->bytes, 1, outbuf->len, fp) != outbuf->len) // read the file in whole |
7 | pmbaty | 1191 | { |
1192 | fclose (fp); |
||
11 | pmbaty | 1193 | outbuf->len = 0; |
7 | pmbaty | 1194 | return (NULL); // short read (errno is set) |
1195 | } |
||
1196 | fclose (fp); // close the file |
||
1197 | |||
15 | pmbaty | 1198 | return (resolved_pathname); // file was read successfully and its content put in databuf with size datalen |
7 | pmbaty | 1199 | } |
1200 | |||
1201 | |||
1 | pmbaty | 1202 | static int fwrite_filecontents (const char *pathname, FILE *fp) |
1203 | { |
||
1204 | // dumps the binary contents of pathname to fp |
||
1205 | |||
1206 | uint8_t *blob_buffer; |
||
1207 | size_t blob_size; |
||
1208 | FILE *blob_fp; |
||
1209 | int ret; |
||
1210 | |||
1211 | blob_fp = fopen (pathname, "rb"); |
||
1212 | if (blob_fp == NULL) |
||
1213 | return (-1); // errno is set |
||
1214 | |||
1215 | fseek (blob_fp, 0, SEEK_END); |
||
1216 | blob_size = ftell (blob_fp); |
||
1217 | blob_buffer = malloc (blob_size); |
||
1218 | if (blob_buffer == NULL) |
||
1219 | { |
||
1220 | fclose (blob_fp); |
||
1221 | return (-1); // errno is set to ENOMEM |
||
1222 | } |
||
1223 | fseek (blob_fp, 0, SEEK_SET); |
||
1224 | fread (blob_buffer, 1, blob_size, blob_fp); |
||
1225 | fclose (blob_fp); |
||
1226 | |||
1227 | ret = (int) fwrite (blob_buffer, 1, blob_size, fp); |
||
8 | pmbaty | 1228 | fflush (fp); // force flush to disk, because the C stream API is *buffered* |
1 | pmbaty | 1229 | free (blob_buffer); |
1230 | return (ret); |
||
1231 | } |
||
1232 | |||
1233 | |||
11 | pmbaty | 1234 | static int relative_offset_of_in (const char *name, const buffer_t *stringbuf) |
10 | pmbaty | 1235 | { |
1236 | int name_len = (int) strlen (name) + 1; |
||
11 | pmbaty | 1237 | WELLMANNERED_ASSERT (name_len < stringbuf->len, "bad call (name longer than string table)"); |
1238 | for (int idx = 0; idx <= stringbuf->len - name_len; idx++) |
||
1239 | if (memcmp (&stringbuf->bytes[idx], name, name_len) == 0) |
||
10 | pmbaty | 1240 | return (idx); |
1241 | WELLMANNERED_ASSERT (false, "bad call (name '%s' not found in string table)", name); |
||
1242 | return (0); |
||
1243 | } |
||
1244 | |||
1245 | |||
1246 | static elf_section_header_t *elf_get_section_header_by_name (const elf_header_t *elf, const char *section_name) |
||
1247 | { |
||
1248 | elf_section_header_t *shdr_shstrtab; // section header of the section header strings table |
||
1249 | elf_section_header_t *shdr; |
||
1250 | size_t table_count; |
||
1251 | size_t table_index; |
||
1252 | char *shstrtab; // section header strings table |
||
1253 | char *name; |
||
1254 | |||
1255 | shdr_shstrtab = (elf_section_header_t *) ((uint8_t *) elf + ELF_GET_NUMERIC (elf, elf, section_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, section_header_item_size) * ELF_GET_NUMERIC (elf, elf, section_header_names_idx)); // quick access to section header for the section that contains the section names |
||
1256 | shstrtab = ((uint8_t *) elf + ELF_GET_NUMERIC (elf, shdr_shstrtab, file_offset)); // locate the start of the strings table that contains the section names |
||
1257 | |||
1258 | // cycle through the sections table |
||
1259 | table_count = ELF_GET_NUMERIC (elf, elf, section_header_table_len); |
||
1260 | for (table_index = 0; table_index < table_count; table_index++) |
||
1261 | { |
||
1262 | shdr = (elf_section_header_t *) ((uint8_t *) elf + ELF_GET_NUMERIC (elf, elf, section_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, section_header_item_size) * table_index); // quick access to section header |
||
1263 | name = &shstrtab[ELF_GET_NUMERIC (elf, shdr, name_offset)]; // peek at its name |
||
1264 | if (strcmp (name, section_name) == 0) |
||
1265 | return (shdr); // if found, return a pointer to this section header |
||
1266 | } |
||
1267 | |||
1268 | return (NULL); // section not found |
||
1269 | } |
||
1270 | |||
1271 | |||
1 | pmbaty | 1272 | static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp) |
1273 | { |
||
7 | pmbaty | 1274 | // writes a directory entry in the image filesystem file pointed to by fp (or fakes so if fp is NULL) |
1275 | // and return the number of bytes written (or that would have been written) |
||
1276 | |||
1 | pmbaty | 1277 | static const uint8_t zeropad_buffer[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; |
1278 | |||
1279 | size_t datalen; |
||
1280 | size_t count; |
||
1281 | |||
1282 | count = 0; |
||
1283 | if (fp != NULL) |
||
8 | pmbaty | 1284 | fwrite_or_die (&fsentry->header, 1, sizeof (fsentry->header), fp); // write the entry header (PACKED STRUCT) |
1 | pmbaty | 1285 | count += sizeof (fsentry->header); |
1286 | if (S_ISREG (fsentry->header.mode)) |
||
1287 | { |
||
1288 | if (fp != NULL) |
||
1289 | { |
||
8 | pmbaty | 1290 | fwrite_or_die (&fsentry->u.file.offset, 1, sizeof (uint32_t), fp); // write offset |
1291 | fwrite_or_die (&fsentry->u.file.size, 1, sizeof (uint32_t), fp); // write size |
||
1 | pmbaty | 1292 | } |
1293 | count += 2 * sizeof (uint32_t); |
||
1294 | datalen = strlen (fsentry->u.file.path) + 1; |
||
1295 | if (fp != NULL) |
||
8 | pmbaty | 1296 | fwrite_or_die (fsentry->u.file.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash) |
1 | pmbaty | 1297 | count += datalen; |
1298 | } |
||
1299 | else if (S_ISDIR (fsentry->header.mode)) |
||
1300 | { |
||
1301 | datalen = strlen (fsentry->u.dir.path) + 1; |
||
1302 | if (fp != NULL) |
||
8 | pmbaty | 1303 | fwrite_or_die (fsentry->u.dir.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash) |
1 | pmbaty | 1304 | count += datalen; |
1305 | } |
||
1306 | else if (S_ISLNK (fsentry->header.mode)) |
||
1307 | { |
||
1308 | if (fp != NULL) |
||
1309 | { |
||
8 | pmbaty | 1310 | fwrite_or_die (&fsentry->u.symlink.sym_offset, 1, sizeof (uint16_t), fp); // write offset |
1311 | fwrite_or_die (&fsentry->u.symlink.sym_size, 1, sizeof (uint16_t), fp); // write size |
||
1 | pmbaty | 1312 | } |
1313 | count += 2 * sizeof (uint16_t); |
||
1314 | datalen = strlen (fsentry->u.symlink.path) + 1; |
||
1315 | if (fp != NULL) |
||
8 | pmbaty | 1316 | fwrite_or_die (fsentry->u.symlink.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash) |
1 | pmbaty | 1317 | count += datalen; |
1318 | datalen = strlen (fsentry->u.symlink.contents) + 1; |
||
1319 | if (fp != NULL) |
||
8 | pmbaty | 1320 | fwrite_or_die (fsentry->u.symlink.contents, 1, (size_t) datalen, fp); // write null-terminated symlink contents |
1 | pmbaty | 1321 | count += datalen; |
1322 | } |
||
1323 | else |
||
1324 | { |
||
1325 | if (fp != NULL) |
||
1326 | { |
||
8 | pmbaty | 1327 | fwrite_or_die (&fsentry->u.device.dev, 1, sizeof (uint32_t), fp); // write dev number |
1328 | fwrite_or_die (&fsentry->u.device.rdev, 1, sizeof (uint32_t), fp); // write rdev number |
||
1 | pmbaty | 1329 | } |
1330 | count += 2 * sizeof (uint32_t); |
||
1331 | datalen = strlen (fsentry->u.device.path) + 1; |
||
1332 | if (fp != NULL) |
||
8 | pmbaty | 1333 | fwrite_or_die (fsentry->u.device.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash) |
1 | pmbaty | 1334 | count += datalen; |
1335 | } |
||
1336 | |||
15 | pmbaty | 1337 | WELLMANNERED_ASSERT (count <= fsentry->header.size, "attempt to write invalid dirent (claimed size %zd, written size %zd). Aborting.", (size_t) fsentry->header.size, count); |
7 | pmbaty | 1338 | if (count < fsentry->header.size) |
1339 | { |
||
1340 | if (fp != NULL) |
||
8 | pmbaty | 1341 | fwrite_or_die (zeropad_buffer, 1, fsentry->header.size - count, fp); // pad as necessary |
7 | pmbaty | 1342 | count += fsentry->header.size - count; |
1343 | } |
||
1 | pmbaty | 1344 | |
1345 | return (count); |
||
1346 | } |
||
1347 | |||
1348 | |||
7 | pmbaty | 1349 | static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, parms_t *entry_parms, const char *stored_pathname, const char *buildhost_pathname) |
1 | pmbaty | 1350 | { |
11 | pmbaty | 1351 | #define ADD_NAME_TO_STRINGTABLE(name,strtab) do { \ |
10 | pmbaty | 1352 | name_len = strlen ((name)) + 1; \ |
11 | pmbaty | 1353 | reallocated_ptr = realloc ((strtab).bytes, (strtab).len + name_len); \ |
10 | pmbaty | 1354 | WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); \ |
11 | pmbaty | 1355 | (strtab).bytes = reallocated_ptr; \ |
1356 | memcpy (&(strtab).bytes[(strtab).len], (name), name_len); \ |
||
1357 | (strtab).len += name_len; \ |
||
10 | pmbaty | 1358 | } while (0) |
11 | pmbaty | 1359 | #define APPEND_SECTION_DATA(section,sectionhdr_offset) do { \ |
1360 | memcpy (&entry_parms->data.bytes[entry_parms->data.len], (section).bytes, (section).len); /* write section in place */ \ |
||
1361 | free ((section).bytes); /* free it */ \ |
||
1362 | new_shdr = (elf_section_header_t *) &new_shtab.bytes[(sectionhdr_offset)]; /* now fix this section header */ \ |
||
1363 | ELF_SET_NUMERIC (elf, new_shdr, file_offset, entry_parms->data.len); /* fix section offset in the new section headers table */ \ |
||
1364 | entry_parms->data.len += (section).len; /* update new ELF file length */ \ |
||
1365 | } while (0) |
||
10 | pmbaty | 1366 | |
15 | pmbaty | 1367 | static thread_local char candidate_pathname[MAXPATHLEN]; |
1 | pmbaty | 1368 | static int inode_count = 0; // will be preincremented each time this function is called |
1369 | |||
7 | pmbaty | 1370 | const char *original_stored_pathname = NULL; |
1371 | const elf_dynamic_section_entry_t *dynamic_entry; // dynamic section entry |
||
1372 | const elf_section_header_t *shdr_dynstr; // dynamic strings |
||
1373 | const elf_section_header_t *shdr_dynamic; // dynamic section |
||
1374 | const elf_section_header_t *shdr; |
||
10 | pmbaty | 1375 | elf_section_header_t *new_shdr; |
1376 | size_t new_qnxinfo_shdr_offset; |
||
1377 | size_t new_debuglink_shdr_offset; |
||
1378 | size_t new_qnxusage_shdr_offset; |
||
1379 | size_t new_buildid_shdr_offset; |
||
1380 | size_t new_shstrtab_shdr_offset; |
||
1381 | elf_program_header_t *phdr; |
||
7 | pmbaty | 1382 | const char *canonical_dylib_name; |
1383 | const char *dynamic_strings; // strings table of the ".dynamic" section |
||
1384 | const char *last_dirsep; |
||
10 | pmbaty | 1385 | elf_header_t *elf; |
11 | pmbaty | 1386 | buffer_t new_shtab = { NULL, 0 }; |
1387 | buffer_t elfsection_qnxinfo = { NULL, 0 }; |
||
1388 | buffer_t elfsection_qnxusage = { NULL, 0 }; |
||
1389 | buffer_t elfsection_debuglink = { NULL, 0 }; |
||
1390 | buffer_t elfsection_buildid = { NULL, 0 }; |
||
1391 | buffer_t elfsection_shstrtab = { NULL, 0 }; |
||
15 | pmbaty | 1392 | char *global_envstring = NULL; |
1393 | size_t global_envstring_len = 0; |
||
1394 | char *startup_name = NULL; |
||
1395 | char *procnto_name = NULL; |
||
7 | pmbaty | 1396 | char *resolved_pathname; |
1 | pmbaty | 1397 | void *reallocated_ptr; |
7 | pmbaty | 1398 | void *old_data; |
1 | pmbaty | 1399 | struct stat stat_buf; |
10 | pmbaty | 1400 | size_t new_shdrtable_offset; |
1401 | size_t end_padding_offset; |
||
7 | pmbaty | 1402 | size_t table_index; |
1403 | size_t table_count; |
||
10 | pmbaty | 1404 | size_t name_len; |
1 | pmbaty | 1405 | fsentry_t *fsentry; |
1406 | |||
1407 | if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ? |
||
1408 | { |
||
15 | pmbaty | 1409 | LOG_INFO ("directory: ino 0x%x uid %d gid %d mode 0%o path \"%s\"", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname); |
1 | pmbaty | 1410 | } |
1411 | else if (S_ISREG (entry_parms->st_mode)) // else are we storing a regular file ? |
||
1412 | { |
||
7 | pmbaty | 1413 | if (strcmp (stored_pathname, "/proc/boot/boot") == 0) // is it the kernel ? |
1 | pmbaty | 1414 | { |
7 | pmbaty | 1415 | // HACK: for now just consider the kernel as a binary blob |
1416 | // FIXME: reimplement properly |
||
15 | pmbaty | 1417 | #ifdef PROCNTO_WIP // FIXME: segment corruption somewhere! |
1418 | char *linebit_start; |
||
1419 | char *content_line; |
||
1420 | char *write_ptr; |
||
1421 | char *token; |
||
1422 | char *value; |
||
1423 | bool is_quoted_context; |
||
1424 | bool was_string_split; |
||
1425 | |||
1426 | // parse each line of contents |
||
1427 | WELLMANNERED_ASSERT (entry_parms->data.len > 0, "kernel specification without inline contents"); |
||
1428 | for (content_line = strtok (entry_parms->data.bytes, "\n"); content_line != NULL; content_line = strtok (NULL, "\n")) |
||
1429 | { |
||
1430 | while (isspace (*content_line)) |
||
1431 | content_line++; // skip leading spaces |
||
1432 | if ((*content_line == '#') || (*content_line == 0)) |
||
1433 | continue; // skip comments and empty lines |
||
1434 | |||
1435 | // format of a line: [attributes] [env assignation] [...] [executable] [arg] [...] [comment] |
||
1436 | // example: "[uid=0 gid=0 perms=0700] CONFIG_PATH=/proc/boot:/etc procnto-smp-instr -v -mr -d 0777 -u 0777" |
||
1437 | //LOG_DEBUG ("parsing line: %s", content_line); |
||
1438 | |||
1439 | // does this line start with an attribute block ? |
||
1440 | if (*content_line == '[') |
||
1441 | { |
||
1442 | content_line++; // skip the leading square bracket |
||
1443 | linebit_start = content_line; // remember where it starts |
||
1444 | is_quoted_context = false; // reach the next unescaped closing square bracket that is not between quotes |
||
1445 | while ((*content_line != 0) && !((*content_line == ']') && (content_line[-1] != '\\') && !is_quoted_context)) |
||
1446 | { |
||
1447 | if (*content_line == '"') |
||
1448 | is_quoted_context ^= true; // remember when we're between quotes |
||
1449 | else if (!is_quoted_context && (*content_line == ' ')) |
||
1450 | *content_line = RECORD_SEP; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting |
||
1451 | content_line++; // reach the next unescaped closing square bracket |
||
1452 | } |
||
1453 | if (*content_line != ']') |
||
1454 | { |
||
1455 | LOG ("warning", 0, "syntax error in \"%s\" line %d: unterminated attributes block (skipping)", buildfile_pathname, lineno); |
||
1456 | continue; // invalid attribute block, skip line |
||
1457 | } |
||
1458 | *content_line = 0; // end the attribute block so that it is a parsable C string |
||
1459 | |||
1460 | // now parse the attribute tokens (NOTE: THE LIST OF ALLOWED ATTRIBUTES HERE IS NOT DOCUMENTED) |
||
1461 | token = strtok (linebit_start, RECORD_SEP_STR); |
||
1462 | while (token != NULL) |
||
1463 | { |
||
1464 | #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0) |
||
1465 | if (strncmp (token, "uid=", 4) == 0) { REACH_TOKEN_VALUE (); entry_parms->uid = (int) read_integer (value); } |
||
1466 | else if (strncmp (token, "gid=", 4) == 0) { REACH_TOKEN_VALUE (); entry_parms->gid = (int) read_integer (value); } |
||
1467 | else if (strncmp (token, "perms=", 6) == 0) { REACH_TOKEN_VALUE (); entry_parms->perms = (int) read_integer (value); } |
||
1468 | else if (strncmp (token, "prefix=", 7) == 0) { REACH_TOKEN_VALUE (); strcpy (entry_parms->prefix, (*value == '/' ? value + 1 : value)); } // skip possible leading slash in prefix |
||
1469 | else if (strcmp (token, "+followlink") == 0) entry_parms->should_follow_symlinks = true; |
||
1470 | else if (strcmp (token, "-followlink") == 0) entry_parms->should_follow_symlinks = false; |
||
1471 | else if (strcmp (token, "+keeplinked") == 0) entry_parms->should_keep_ld_output = true; |
||
1472 | else if (strcmp (token, "-keeplinked") == 0) entry_parms->should_keep_ld_output = false; |
||
1473 | else LOG_WARNING ("unimplemented bootstrap executable attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, token); |
||
1474 | #undef REACH_TOKEN_VALUE |
||
1475 | token = strtok (NULL, RECORD_SEP_STR); // proceed to next attribute token |
||
1476 | } |
||
1477 | |||
1478 | content_line++; // reach the next character |
||
1479 | while ((*content_line != 0) && isspace (*content_line)) |
||
1480 | content_line++; // skip leading spaces |
||
1481 | } // end of "this line starts with an attributes block" |
||
1482 | |||
1483 | // there's data in this line. We expect an executable OR a variable name. Read it and unescape escaped characters |
||
1484 | while (*content_line != 0) |
||
1485 | { |
||
1486 | linebit_start = content_line; // remember the name starts here |
||
1487 | write_ptr = linebit_start; |
||
1488 | is_quoted_context = (*content_line == '"'); |
||
1489 | if (is_quoted_context) |
||
1490 | content_line++; // skip a possible initial quote in the name |
||
1491 | while ((*content_line != 0) && ((!is_quoted_context && (*content_line != '=') && !isspace (*content_line)) || (is_quoted_context && (*content_line == '"')))) |
||
1492 | { |
||
1493 | if (*content_line == '\\') |
||
1494 | { |
||
1495 | content_line++; |
||
1496 | *write_ptr++ = *content_line; // unescape characters that are escaped with '\' |
||
1497 | } |
||
1498 | else |
||
1499 | *write_ptr++ = *content_line; |
||
1500 | content_line++; |
||
1501 | } |
||
1502 | |||
1503 | // we reached a closing quote, a space OR an equal sign |
||
1504 | if (*content_line == '=') |
||
1505 | { |
||
1506 | // it's an environment variable assignation |
||
1507 | *write_ptr++ = *content_line++; // skip the equal sign |
||
1508 | is_quoted_context = (*content_line == '"'); |
||
1509 | if (is_quoted_context) |
||
1510 | content_line++; // skip a possible initial quote in the value |
||
1511 | while ((*content_line != 0) && ((!is_quoted_context && (*content_line != '=') && !isspace (*content_line)) || (is_quoted_context && (*content_line == '"')))) |
||
1512 | { |
||
1513 | if (*content_line == '\\') |
||
1514 | { |
||
1515 | content_line++; |
||
1516 | *write_ptr++ = *content_line; // unescape characters that are escaped with '\' |
||
1517 | } |
||
1518 | else |
||
1519 | *write_ptr++ = *content_line; |
||
1520 | content_line++; |
||
1521 | } |
||
1522 | if (*write_ptr != 0) |
||
1523 | { |
||
1524 | *write_ptr = 0; // terminate the string if necessary |
||
1525 | was_string_split = true; |
||
1526 | } |
||
1527 | else |
||
1528 | was_string_split = false; |
||
1529 | if (is_quoted_context && (*content_line == '"')) |
||
1530 | content_line++; // skip a possible final quote |
||
1531 | while ((*content_line != 0) && isspace (*content_line)) |
||
1532 | content_line++; // skip spaces |
||
1533 | |||
1534 | // now linebit_start is of the form "NAME=VALUE" |
||
1535 | LOG_DEBUG ("assignation: [%s]", linebit_start); |
||
1536 | |||
1537 | // TODO: grow global_envstring |
||
1538 | |||
1539 | //reallocated_ptr = realloc (global_envstring, global_envstring_len + strlen ()) |
||
1540 | |||
1541 | if (was_string_split) |
||
1542 | *write_ptr = ' '; // restore string continuity for parsing to continue |
||
1543 | while ((*content_line != 0) && isspace (*content_line)) |
||
1544 | content_line++; // skip spaces |
||
1545 | } |
||
1546 | else // it's either a closing quote or a space |
||
1547 | { |
||
1548 | *write_ptr = 0; // terminate the string |
||
1549 | if (is_quoted_context && (*content_line == '"')) |
||
1550 | content_line++; // skip a possible final quote |
||
1551 | |||
1552 | LOG_DEBUG ("exe name: [%s]", linebit_start); |
||
1553 | |||
1554 | while ((*content_line != 0) && isspace (*content_line)) |
||
1555 | content_line++; // skip leading spaces |
||
1556 | |||
1557 | // it's an executable name. As per QNX docs, the first executable must be startup-*, the last executable must be procnto. |
||
1558 | if (startup_name == NULL) |
||
1559 | startup_name = strdup (linebit_start); |
||
1560 | else |
||
1561 | { |
||
1562 | if (procnto_name != NULL) |
||
1563 | free (procnto_name); |
||
1564 | procnto_name = strdup (linebit_start); |
||
1565 | } |
||
1566 | |||
1567 | if ((*content_line == '#') || (*content_line == 0)) |
||
1568 | break; // if we reach the end of the line, stop parsing |
||
1569 | |||
1570 | // what comes after now must be optional arguments |
||
1571 | while ((*content_line != 0) && isspace (*content_line)) |
||
1572 | content_line++; // skip leading spaces |
||
1573 | |||
1574 | // FIXME: parse executable command-line arguments |
||
1575 | |||
1576 | break; // stop parsing once all the arguments have been read |
||
1577 | } |
||
1578 | } |
||
1579 | } // end of parsing |
||
1580 | free (entry_parms->data.bytes); // free the inline specification once it's parsed |
||
1581 | entry_parms->data.bytes = NULL; |
||
1582 | entry_parms->data.len = 0; |
||
1583 | |||
1584 | WELLMANNERED_ASSERT (startup_name && *startup_name, "the QNX startup executable (startup-*) is missing in this bootstrap inline specification"); |
||
1585 | WELLMANNERED_ASSERT (procnto_name && *procnto_name, "the QNX kernel (procnto-*) is missing in this bootstrap inline specification"); |
||
1586 | |||
1587 | // now we know which startup and procnto executables to use |
||
1588 | LOG_DEBUG ("Startup: %s", startup_name); |
||
1589 | LOG_DEBUG ("Kernel: %s", procnto_name); |
||
1590 | |||
1591 | sprintf (candidate_pathname, "%s/%s", entry_parms->prefix, procnto_name); // fix the entry name |
||
7 | pmbaty | 1592 | stored_pathname = candidate_pathname; |
15 | pmbaty | 1593 | entry_parms->extra_ino_flags |= /*IFS_INO_PROCESSED_ELF | */IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode |
1594 | entry_parms->st_mode = S_IFREG | entry_parms->perms; // apply specified procnto permissions |
||
10 | pmbaty | 1595 | image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1); |
15 | pmbaty | 1596 | |
1597 | static thread_local char linker_pathname[MAXPATHLEN] = ""; |
||
1598 | static thread_local char linker_sysroot_arg[MAXPATHLEN] = ""; |
||
1599 | static thread_local char linker_script_pathname_arg[MAXPATHLEN] = ""; |
||
1600 | static thread_local char procnto_buildhost_pathname[MAXPATHLEN] = ""; |
||
1601 | static thread_local char procnto_sym_filename[MAXPATHLEN] = ""; |
||
1602 | |||
1603 | // construct the arguments that are based on environment variables (infer QNX_HOST from QNX_TARGET) |
||
1604 | #if defined(_WIN32) |
||
1605 | sprintf (linker_pathname, "%s/../../host/win64/x86_64/usr/bin/%s-ld.exe", QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0")); // Win32: note the .exe extension |
||
1606 | #elif defined(__linux__) |
||
1607 | sprintf (linker_pathname, "%s/../../host/linux/x86_64/usr/bin/%s-ld", QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0")); |
||
1608 | #elif defined(__QNXNTO__) |
||
1609 | sprintf (linker_pathname, "%s/../../host/qnx8/x86_64/usr/bin/%s-ld", QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0")); |
||
1610 | #else // wtf are you building this on? |
||
1611 | #error Please port the GNU linker x86_64-pc-nto-qnx8.0.0-ld and aarch64-unknown-nto-qnx8.0.0-ld to your host architecture first before compiling ifstool. |
||
1612 | #endif |
||
1613 | WELLMANNERED_ASSERT (access (linker_pathname, 0) == 0, "host cross-linker for QNX8 \"%s\" not found", linker_pathname); |
||
1614 | sprintf (linker_sysroot_arg, "--sysroot=%s/%s/", QNX_TARGET, image_processor); |
||
1615 | sprintf (linker_script_pathname_arg, "-T%s/%s/lib/nto.link", QNX_TARGET, image_processor); |
||
1616 | |||
1617 | resolved_pathname = read_filecontents (procnto_name, (entry_parms->search[0] != 0 ? entry_parms->search : MKIFS_PATH), NULL); // locate the procnto kernel location, but do not load it |
||
1618 | WELLMANNERED_ASSERT (resolved_pathname, "QNX kernel \"%s\" not found in search path", procnto_name); |
||
1619 | strcpy (procnto_buildhost_pathname, resolved_pathname); |
||
1620 | |||
1621 | sprintf (procnto_sym_filename, "%s.sym", procnto_name); |
||
1622 | |||
1623 | const char *linker_argv[] = // construct the linker invokation argv |
||
1624 | { |
||
1625 | strrchr (linker_pathname, '/') + 1, // "${TARGET_TRIPLE}-ld" |
||
1626 | linker_sysroot_arg, // "--sysroot=${QNX_TARGET}/${TARGET_CPU}/" |
||
1627 | linker_script_pathname_arg, // "-T${QNX_TARGET}/${TARGET_CPU}/lib/nto.link" |
||
1628 | "--section-start", |
||
1629 | ".text=0xffff800000001000", |
||
1630 | "--no-relax", |
||
1631 | procnto_buildhost_pathname, // "${QNX_TARGET}/${TARGET_CPU}/boot/sys/procnto-smp-instr" |
||
1632 | "-o", |
||
1633 | procnto_sym_filename, // "procnto-smp-instr.sym" |
||
1634 | NULL |
||
1635 | }; |
||
1636 | if (verbose_level > 2) |
||
1637 | { |
||
1638 | fprintf (stderr, "ifstool: calling:"); |
||
1639 | for (table_index = 0; table_index < sizeof (linker_argv) / sizeof (linker_argv[0]) - 1; table_index++) |
||
1640 | fprintf (stderr, " '%s'", linker_argv[table_index]); |
||
1641 | fputc ('\n', stderr); |
||
1642 | } |
||
1643 | _spawnv (_P_WAIT, linker_pathname, linker_argv); // spawn the linker and produce a stripped procnto (wait for completion) |
||
1644 | resolved_pathname = read_filecontents (procnto_sym_filename, ".", &entry_parms->data); // locate the output file and load it |
||
1645 | if (resolved_pathname == NULL) |
||
1646 | DIE_WITH_EXITCODE (1, "the host cross-linker failed to produce a readable stripped \"%s\" kernel: %s", procnto_sym_filename, strerror (errno)); |
||
1647 | if (!entry_parms->should_keep_ld_output) |
||
1648 | unlink (procnto_sym_filename); // remove the linker output file if we want to |
||
1649 | #else // !PROCNTO_WIP |
||
1650 | /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */ |
||
1651 | /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */ |
||
1652 | /* HACK */ |
||
1653 | /* HACK */ sprintf (candidate_pathname, "%s/procnto-smp-instr", entry_parms->prefix); // HACK: fix the entry name |
||
1654 | /* HACK */ stored_pathname = candidate_pathname; |
||
1655 | /* HACK */ entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode |
||
1656 | /* HACK */ entry_parms->st_mode = S_IFREG | 0700; // procnto requires 0700 permissions |
||
1657 | /* HACK */ image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1); |
||
1658 | /* HACK */ free (entry_parms->data.bytes); // discard inline contents |
||
1659 | /* HACK */ entry_parms->data.bytes = NULL; |
||
1660 | /* HACK */ entry_parms->data.len = 0; |
||
1661 | /* HACK */ if (read_filecontents (kernelfile_pathname, ".", &entry_parms->data) == NULL) // read kernel file as a precompiled binary blob |
||
1662 | /* HACK */ { |
||
1663 | /* HACK */ fprintf (stderr, "fatal error: unable to read precompiled kernel file \"%s\" specified in --kernelfile argument\n", kernelfile_pathname); |
||
1664 | /* HACK */ exit (1); |
||
1665 | /* HACK */ } |
||
1666 | /* HACK */ |
||
1667 | /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */ |
||
1668 | /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */ |
||
1669 | #endif // PROCNTO_WIP |
||
7 | pmbaty | 1670 | } |
1671 | else if (entry_parms->is_compiled_bootscript) // else is it a startup script ? |
||
1672 | image_bootscript_ino = inode_count + 1; // save boot script inode number for image header |
||
2 | pmbaty | 1673 | |
7 | pmbaty | 1674 | // do we already know the data for this data blob ? |
11 | pmbaty | 1675 | if (entry_parms->data.bytes != NULL) |
7 | pmbaty | 1676 | { |
15 | pmbaty | 1677 | entry_parms->mtime = entry_parms->mtime_for_inline_files; // if so, set it a mtime equal to the mtime to use for inline files |
1678 | LOG_INFO ("file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" blob (len %zd)", entry_parms->extra_ino_flags | (inode_count + 1), entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.len); |
||
7 | pmbaty | 1679 | } |
8 | pmbaty | 1680 | else if (buildhost_pathname != NULL) // else was a source file pathname supplied ? |
1 | pmbaty | 1681 | { |
11 | pmbaty | 1682 | resolved_pathname = read_filecontents (buildhost_pathname, (entry_parms->search[0] != 0 ? entry_parms->search : MKIFS_PATH), &entry_parms->data); // locate the file |
7 | pmbaty | 1683 | if (resolved_pathname == NULL) |
15 | pmbaty | 1684 | DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: %s", buildhost_pathname, buildfile_pathname, lineno, strerror (errno)); |
7 | pmbaty | 1685 | stat (resolved_pathname, &stat_buf); // can't fail |
1686 | if (entry_parms->mtime == UINT32_MAX) |
||
1687 | entry_parms->mtime = (uint32_t) stat_buf.st_mtime; |
||
15 | pmbaty | 1688 | LOG_INFO ("file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" buildhost_file \"%s\" (len %zd)", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, buildhost_pathname, entry_parms->data.len); |
8 | pmbaty | 1689 | } |
1 | pmbaty | 1690 | |
9 | pmbaty | 1691 | // is the file we're storing an ELF file ? |
11 | pmbaty | 1692 | if ((entry_parms->data.len > 52) // file is big enough to contain an ELF header |
1693 | && ((elf = (elf_header_t *) entry_parms->data.bytes) != NULL) // cast (necessary true) |
||
10 | pmbaty | 1694 | && (memcmp (ELF_GET_STRING (elf, elf, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic |
7 | pmbaty | 1695 | { |
9 | pmbaty | 1696 | // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ? |
10 | pmbaty | 1697 | if ((ELF_GET_NUMERIC (elf, elf, type) == 3) && entry_parms->should_autosymlink_dylib) |
7 | pmbaty | 1698 | { |
9 | pmbaty | 1699 | // we need to find the SONAME of this library |
1700 | canonical_dylib_name = NULL; |
||
1 | pmbaty | 1701 | |
9 | pmbaty | 1702 | // locate the sections we need (the dynamic section and its strings table) |
10 | pmbaty | 1703 | shdr_dynamic = elf_get_section_header_by_name (elf, ".dynamic"); |
1704 | shdr_dynstr = elf_get_section_header_by_name (elf, ".dynstr"); |
||
7 | pmbaty | 1705 | |
9 | pmbaty | 1706 | // make sure we have both the dynamic section header and its own strings table header |
1707 | if ((shdr_dynamic != NULL) && (shdr_dynstr != NULL)) |
||
1708 | { |
||
11 | pmbaty | 1709 | dynamic_strings = (char *) &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr_dynstr, file_offset)]; // quick access to dynamic sections strings table |
7 | pmbaty | 1710 | |
9 | pmbaty | 1711 | // walk through the dynamic section, look for the DT_SONAME entry |
11 | pmbaty | 1712 | for (dynamic_entry = (elf_dynamic_section_entry_t *) &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr_dynamic, file_offset)]; |
10 | pmbaty | 1713 | (ELF_GET_NUMERIC (elf, dynamic_entry, tag) != ELF_DT_NULL); |
9 | pmbaty | 1714 | dynamic_entry = (elf_dynamic_section_entry_t *) ((uint8_t *) dynamic_entry + ELF_STRUCT_SIZE (elf, dynamic_entry))) |
10 | pmbaty | 1715 | if (ELF_GET_NUMERIC (elf, dynamic_entry, tag) == ELF_DT_SONAME) |
9 | pmbaty | 1716 | { |
11 | pmbaty | 1717 | canonical_dylib_name = dynamic_strings + ELF_GET_NUMERIC (elf, dynamic_entry, value); |
9 | pmbaty | 1718 | break; |
1719 | } |
||
1720 | |||
1721 | // do we have it ? |
||
1722 | if ((canonical_dylib_name != NULL) && (canonical_dylib_name[0] != 0)) |
||
7 | pmbaty | 1723 | { |
9 | pmbaty | 1724 | sprintf (candidate_pathname, "%s/%s", entry_parms->prefix, canonical_dylib_name); |
1725 | if (strcmp (candidate_pathname, stored_pathname) != 0) // claimed dylib name differs from passed name ? |
||
1726 | { |
||
1727 | original_stored_pathname = stored_pathname; // if so, remember to create a symlink here |
||
1728 | stored_pathname = candidate_pathname; |
||
1729 | } |
||
7 | pmbaty | 1730 | } |
1 | pmbaty | 1731 | } |
9 | pmbaty | 1732 | } // end if the file we're storing is a dylib |
10 | pmbaty | 1733 | |
1734 | // now strip this ELF file if necessary |
||
1735 | if (!(entry_parms->extra_ino_flags & IFS_INO_PROCESSED_ELF)) |
||
1736 | { |
||
1737 | // NOTE: for each ELF file, mkifs |
||
15 | pmbaty | 1738 | // -> alters the program header table and offsets each p_addr (physical address) member by <image_base> plus the current file offset (this cannot be done right now, will need to be done once they are known) |
10 | pmbaty | 1739 | // -> throws away and reconstructs the sections table by keeping only the sections that are in the program header, and writes the section table at the start of the first thrown-away section |
11 | pmbaty | 1740 | // FIXME: what if a thrown away section is located between two program segments ? are they collapsed, moving the segments beyond it one slot down ? |
10 | pmbaty | 1741 | |
1742 | // reconstructed ELF: |
||
1743 | // ==== START OF FILE ==== |
||
1744 | // ELF header |
||
1745 | // program header table |
||
11 | pmbaty | 1746 | // (same sections, just p_addr offset changed) |
10 | pmbaty | 1747 | // section data 5 (named ".note.gnu.build-id") |
1748 | // "............GNU....ZY.....c.o..l" |
||
1749 | // PROGRAM |
||
1750 | // sections table |
||
1751 | // + section 1: ALL ZEROES |
||
1752 | // + section 2: fileoffs 0x21a8 size 0xfd --> "QNX_info" --> QNX binary description: "NAME=pci_debug2.so.3.0\nDESCRIPTION=PCI Server System Debug Module\nDATE=2023/11/19-10:01:13-EST\nSTATE=lookup\nHOST=docker-n1.bts.rim.net\nUSER=builder\nVERSION=QNXOS_main\nTAGID=QNXOS_800-135\nPACKAGE=com.qnx.qnx800.target.pci.debug/3.0.0.00135T202311191043L\n" |
||
15 | pmbaty | 1753 | // + section 3: fileoffs 0x22a5 size 0x1c --> ".gnu_debuglink" --> indicates the debug file and its checksum: "pci_debug2.so.3.0.sym" "\0\0\0" "VX2p" |
10 | pmbaty | 1754 | // + section 4: fileoffs 0x22c1 size 0x2ad --> "QNX_usage" --> HELP TEXT: "\n-------------------------------------------------------------------------------\n%C\n\nThis module implements debug logging for all PCI server modules. It is\nincluded by setting the environment variable PCI_DEBUG_MODULE and uses\nthe slogger2 APIs.\nNOTE:.On systems which support slogger2, you are encouraged to use this module.instead of pci_debug.so...Release History.---------------..3.0 - This module is functionally equivalent to the previous 2.x version. however it is incompatible with all pre v3.x PCI components..2.1 - fixes a bug whereby if slogger2 is not running and the PCI_DEBUG_MODULE. environment variable is set, the client will SIGSEGV..2.0 - initial release.." |
1755 | // + section 5: fileoffs 0x190 size 0x32 --> ".note.gnu.build-id" --> GNU build ID |
||
1756 | // + section 6: fileoffs 0x256e size 0x40 --> ".shstrtab" --> sections names strings table |
||
1757 | // section data 2 (named "QNX_info") |
||
1758 | // (QNX binary description) |
||
1759 | // section data 3 (named ".gnu_debuglink") |
||
1760 | // (debug file) |
||
1761 | // section data 4 (named "QNX_usage") |
||
1762 | // (help text) |
||
1763 | // section data 6 (named ".shstrtab") |
||
1764 | // "\0" |
||
1765 | // ".shstrtab\0" |
||
1766 | // "QNX_info\0" |
||
1767 | // ".gnu_debuglink\0" |
||
1768 | // "QNX_usage\0" |
||
1769 | // ".note.gnu.build-id\0" |
||
1770 | // ==== END OF FILE ==== |
||
1771 | |||
11 | pmbaty | 1772 | // parse the program header table, and measure the farthest offset known by this table where we'll write the reconstructed section headers table |
10 | pmbaty | 1773 | |
1774 | new_shdrtable_offset = 0; |
||
1775 | table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); |
||
1776 | for (table_index = 0; table_index < table_count; table_index++) |
||
1777 | { |
||
11 | pmbaty | 1778 | phdr = (elf_program_header_t *) &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header |
10 | pmbaty | 1779 | if (ELF_GET_NUMERIC (elf, phdr, file_offset) + ELF_GET_NUMERIC (elf, phdr, size_in_file) > new_shdrtable_offset) |
1780 | new_shdrtable_offset = ELF_GET_NUMERIC (elf, phdr, file_offset) + ELF_GET_NUMERIC (elf, phdr, size_in_file); |
||
1781 | } |
||
1782 | |||
11 | pmbaty | 1783 | // re-create the section header table |
10 | pmbaty | 1784 | |
11 | pmbaty | 1785 | elfsection_shstrtab.bytes = malloc (1); // initialize an empty section headers strings table |
1786 | WELLMANNERED_ASSERT (elfsection_shstrtab.bytes, "out of memory"); |
||
1787 | elfsection_shstrtab.bytes[0] = 0; |
||
1788 | elfsection_shstrtab.len = 1; |
||
1789 | ADD_NAME_TO_STRINGTABLE (".shstrtab", elfsection_shstrtab); |
||
10 | pmbaty | 1790 | |
11 | pmbaty | 1791 | new_shtab.bytes = malloc (ELF_STRUCT_SIZE (elf, shdr)); // prepare a section headers table with just the default entry |
1792 | WELLMANNERED_ASSERT (new_shtab.bytes, "out of memory"); |
||
1793 | memset (new_shtab.bytes, 0, ELF_STRUCT_SIZE (elf, shdr)); // the first section header is always zerofilled |
||
1794 | new_shtab.len = ELF_STRUCT_SIZE (elf, shdr); // and remember how big the section headers table is now |
||
10 | pmbaty | 1795 | if ((shdr = elf_get_section_header_by_name (elf, "QNX_info")) != NULL) |
1796 | { |
||
1797 | if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it |
||
1798 | { |
||
11 | pmbaty | 1799 | elfsection_qnxinfo.len = ELF_GET_NUMERIC (elf, shdr, size); |
1800 | elfsection_qnxinfo.bytes = malloc (elfsection_qnxinfo.len); |
||
1801 | WELLMANNERED_ASSERT (elfsection_qnxinfo.bytes, "out of memory"); |
||
1802 | memcpy (elfsection_qnxinfo.bytes, &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_qnxinfo.len); |
||
10 | pmbaty | 1803 | } |
11 | pmbaty | 1804 | reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more |
10 | pmbaty | 1805 | WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); |
11 | pmbaty | 1806 | new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer |
1807 | new_qnxinfo_shdr_offset = new_shtab.len; // remember the new offset of this section header |
||
1808 | new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now |
||
10 | pmbaty | 1809 | |
11 | pmbaty | 1810 | new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_qnxinfo_shdr_offset]; // now populate this section header |
1811 | ADD_NAME_TO_STRINGTABLE ("QNX_info", elfsection_shstrtab); |
||
1812 | ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in ("QNX_info", &elfsection_shstrtab)); // update the relative offset of the section name |
||
10 | pmbaty | 1813 | ELF_SET_NUMERIC (elf, new_shdr, type, ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type |
1814 | ELF_SET_NUMERIC (elf, new_shdr, flags, ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags |
||
1815 | ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address |
||
1816 | ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move |
||
1817 | ELF_SET_NUMERIC (elf, new_shdr, size, ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size |
||
12 | pmbaty | 1818 | ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (which should be zero anyway) |
10 | pmbaty | 1819 | ELF_SET_NUMERIC (elf, new_shdr, info, ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info |
1820 | ELF_SET_NUMERIC (elf, new_shdr, alignment, ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment |
||
1821 | ELF_SET_NUMERIC (elf, new_shdr, entry_size, ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size |
||
1822 | } |
||
1823 | if ((shdr = elf_get_section_header_by_name (elf, ".gnu_debuglink")) != NULL) |
||
1824 | { |
||
1825 | if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it |
||
1826 | { |
||
11 | pmbaty | 1827 | elfsection_debuglink.len = ELF_GET_NUMERIC (elf, shdr, size); |
1828 | elfsection_debuglink.bytes = malloc (elfsection_debuglink.len); |
||
1829 | WELLMANNERED_ASSERT (elfsection_debuglink.bytes, "out of memory"); |
||
1830 | memcpy (elfsection_debuglink.bytes, &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_debuglink.len); |
||
10 | pmbaty | 1831 | } |
11 | pmbaty | 1832 | reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more |
10 | pmbaty | 1833 | WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); |
11 | pmbaty | 1834 | new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer |
1835 | new_debuglink_shdr_offset = new_shtab.len; // remember the new offset of this section header |
||
1836 | new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now |
||
10 | pmbaty | 1837 | |
11 | pmbaty | 1838 | new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_debuglink_shdr_offset]; // now populate this section header |
1839 | ADD_NAME_TO_STRINGTABLE (".gnu_debuglink", elfsection_shstrtab); |
||
1840 | ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in (".gnu_debuglink", &elfsection_shstrtab)); // update the relative offset of the section name |
||
10 | pmbaty | 1841 | ELF_SET_NUMERIC (elf, new_shdr, type, ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type |
1842 | ELF_SET_NUMERIC (elf, new_shdr, flags, ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags |
||
1843 | ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address |
||
1844 | ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move |
||
1845 | ELF_SET_NUMERIC (elf, new_shdr, size, ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size |
||
12 | pmbaty | 1846 | ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (which should be zero anyway) |
10 | pmbaty | 1847 | ELF_SET_NUMERIC (elf, new_shdr, info, ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info |
1848 | ELF_SET_NUMERIC (elf, new_shdr, alignment, ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment |
||
1849 | ELF_SET_NUMERIC (elf, new_shdr, entry_size, ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size |
||
1850 | } |
||
1851 | if ((shdr = elf_get_section_header_by_name (elf, "QNX_usage")) != NULL) |
||
1852 | { |
||
1853 | if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it |
||
1854 | { |
||
11 | pmbaty | 1855 | elfsection_qnxusage.len = ELF_GET_NUMERIC (elf, shdr, size); |
1856 | elfsection_qnxusage.bytes = malloc (elfsection_qnxusage.len); |
||
1857 | WELLMANNERED_ASSERT (elfsection_qnxusage.bytes, "out of memory"); |
||
1858 | memcpy (elfsection_qnxusage.bytes, &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_qnxusage.len); |
||
10 | pmbaty | 1859 | } |
11 | pmbaty | 1860 | reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more |
10 | pmbaty | 1861 | WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); |
11 | pmbaty | 1862 | new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer |
1863 | new_qnxusage_shdr_offset = new_shtab.len; // remember the new offset of this section header |
||
1864 | new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now |
||
10 | pmbaty | 1865 | |
11 | pmbaty | 1866 | new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_qnxusage_shdr_offset]; // now populate this section header |
1867 | ADD_NAME_TO_STRINGTABLE ("QNX_usage", elfsection_shstrtab); |
||
1868 | ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in ("QNX_usage", &elfsection_shstrtab)); // update the relative offset of the section name |
||
10 | pmbaty | 1869 | ELF_SET_NUMERIC (elf, new_shdr, type, ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type |
1870 | ELF_SET_NUMERIC (elf, new_shdr, flags, ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags |
||
1871 | ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address |
||
1872 | ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move |
||
1873 | ELF_SET_NUMERIC (elf, new_shdr, size, ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size |
||
12 | pmbaty | 1874 | ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (which should be zero anyway) |
10 | pmbaty | 1875 | ELF_SET_NUMERIC (elf, new_shdr, info, ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info |
1876 | ELF_SET_NUMERIC (elf, new_shdr, alignment, ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment |
||
1877 | ELF_SET_NUMERIC (elf, new_shdr, entry_size, ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size |
||
1878 | } |
||
1879 | if ((shdr = elf_get_section_header_by_name (elf, ".note.gnu.build-id")) != NULL) |
||
1880 | { |
||
1881 | if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it |
||
1882 | { |
||
11 | pmbaty | 1883 | elfsection_buildid.len = ELF_GET_NUMERIC (elf, shdr, size); |
1884 | elfsection_buildid.bytes = malloc (elfsection_buildid.len); |
||
1885 | WELLMANNERED_ASSERT (elfsection_buildid.bytes, "out of memory"); |
||
1886 | memcpy (elfsection_buildid.bytes, &entry_parms->data.bytes[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_buildid.len); |
||
10 | pmbaty | 1887 | } |
11 | pmbaty | 1888 | reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more |
10 | pmbaty | 1889 | WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); |
11 | pmbaty | 1890 | new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer |
1891 | new_buildid_shdr_offset = new_shtab.len; // remember the new offset of this section header |
||
1892 | new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now |
||
10 | pmbaty | 1893 | |
11 | pmbaty | 1894 | new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_buildid_shdr_offset]; // now populate this section header |
1895 | ADD_NAME_TO_STRINGTABLE (".note.gnu.build-id", elfsection_shstrtab); |
||
1896 | ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in (".note.gnu.build-id", &elfsection_shstrtab)); // update the relative offset of the section name |
||
10 | pmbaty | 1897 | ELF_SET_NUMERIC (elf, new_shdr, type, ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type |
1898 | ELF_SET_NUMERIC (elf, new_shdr, flags, ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags |
||
1899 | ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address |
||
1900 | ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move |
||
1901 | ELF_SET_NUMERIC (elf, new_shdr, size, ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size |
||
12 | pmbaty | 1902 | ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (which should be zero anyway) |
10 | pmbaty | 1903 | ELF_SET_NUMERIC (elf, new_shdr, info, ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info |
1904 | ELF_SET_NUMERIC (elf, new_shdr, alignment, ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment |
||
1905 | ELF_SET_NUMERIC (elf, new_shdr, entry_size, ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size |
||
1906 | } |
||
11 | pmbaty | 1907 | reallocated_ptr = realloc (new_shtab.bytes, new_shtab.len + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more |
10 | pmbaty | 1908 | WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); |
11 | pmbaty | 1909 | new_shtab.bytes = reallocated_ptr; // reallocation succeeded, save the new pointer |
1910 | new_shstrtab_shdr_offset = new_shtab.len; // remember the new offset of this section header |
||
1911 | new_shtab.len += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now |
||
10 | pmbaty | 1912 | |
11 | pmbaty | 1913 | new_shdr = (elf_section_header_t *) &new_shtab.bytes[new_shstrtab_shdr_offset]; // now populate this section header |
1914 | ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in (".shstrtab", &elfsection_shstrtab)); // update the relative offset of the section name |
||
10 | pmbaty | 1915 | ELF_SET_NUMERIC (elf, new_shdr, type, ELF_SECTIONTYPE_STRINGTABLE); // section type (SHT_STRTAB) |
1916 | ELF_SET_NUMERIC (elf, new_shdr, flags, 0); // section flags (we could set SHF_STRINGS i.e. 0x20 here, but mkifs does not, so mimic that) |
||
1917 | ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, 0); // this section does not need to be mapped |
||
11 | pmbaty | 1918 | ELF_SET_NUMERIC (elf, new_shdr, file_offset, WILL_BE_FILLED_LATER); // will be filled once we know it |
1919 | ELF_SET_NUMERIC (elf, new_shdr, size, elfsection_shstrtab.len); // section size |
||
10 | pmbaty | 1920 | ELF_SET_NUMERIC (elf, new_shdr, linked_index, 0); // this section is not linked to any other |
1921 | ELF_SET_NUMERIC (elf, new_shdr, info, 0); // this section has no additional info |
||
1922 | ELF_SET_NUMERIC (elf, new_shdr, alignment, 1); // this section is byte-aligned |
||
1923 | ELF_SET_NUMERIC (elf, new_shdr, entry_size, 0); // this section is not a table, so entry_size is zero |
||
1924 | |||
1925 | // jump over the new section headers table and write the sections that need to be relocated after the section headers table |
||
11 | pmbaty | 1926 | entry_parms->data.len = new_shdrtable_offset + new_shtab.len; // assume there are no sections beyond the section headers table until known otherwise |
1927 | if (elfsection_qnxinfo.bytes != NULL) |
||
1928 | APPEND_SECTION_DATA (elfsection_qnxinfo, new_qnxinfo_shdr_offset); // write "QNX_info" section data if we have such a section |
||
1929 | if (elfsection_debuglink.bytes != NULL) |
||
1930 | APPEND_SECTION_DATA (elfsection_debuglink, new_debuglink_shdr_offset); // write ".gnu_debuglink" section data if we have such a section |
||
1931 | if (elfsection_qnxusage.bytes != NULL) |
||
1932 | APPEND_SECTION_DATA (elfsection_qnxusage, new_qnxusage_shdr_offset); // write "QNX_usage" section data if we have such a section |
||
1933 | if (elfsection_buildid.bytes != NULL) |
||
1934 | APPEND_SECTION_DATA (elfsection_buildid, new_buildid_shdr_offset); // write ".note.gnu.build-id" section data if we have such a section |
||
1935 | APPEND_SECTION_DATA (elfsection_shstrtab, new_shstrtab_shdr_offset); // write the section header strings table as the last section |
||
10 | pmbaty | 1936 | |
1937 | // now write the section headers table |
||
11 | pmbaty | 1938 | memcpy (&entry_parms->data.bytes[new_shdrtable_offset], new_shtab.bytes, new_shtab.len); |
1939 | free (new_shtab.bytes); // free it |
||
10 | pmbaty | 1940 | |
1941 | // and finally fix the ELF header |
||
1942 | ELF_SET_NUMERIC (elf, elf, section_header_table_offset, new_shdrtable_offset); |
||
11 | pmbaty | 1943 | ELF_SET_NUMERIC (elf, elf, section_header_table_len, new_shtab.len / ELF_STRUCT_SIZE (elf, shdr)); |
1944 | ELF_SET_NUMERIC (elf, elf, section_header_names_idx, new_shtab.len / ELF_STRUCT_SIZE (elf, shdr) - 1); // the section headers strings table is the last section |
||
10 | pmbaty | 1945 | |
1946 | // align size with page size (4096 on x86, 16k on ARM) |
||
11 | pmbaty | 1947 | end_padding_offset = entry_parms->data.len; |
10 | pmbaty | 1948 | if (ELF_GET_NUMERIC (elf, elf, instruction_set) == ELF_MACHINE_X86_64) |
11 | pmbaty | 1949 | entry_parms->data.len = ROUND_TO_UPPER_MULTIPLE (end_padding_offset, 4 * 1024); // 4 kb pages on Intel processors |
10 | pmbaty | 1950 | else if (ELF_GET_NUMERIC (elf, elf, instruction_set) == ELF_MACHINE_AARCH64) |
11 | pmbaty | 1951 | entry_parms->data.len = ROUND_TO_UPPER_MULTIPLE (end_padding_offset, 16 * 1024); // 16 kb pages on ARM64 |
10 | pmbaty | 1952 | else |
15 | pmbaty | 1953 | DIE_WITH_EXITCODE (1, "this ELF file \"%s\" does not belong to an architecture supported by ifstool (neither x86_64, nor aarch64)", stored_pathname); |
1954 | |||
11 | pmbaty | 1955 | memset (&entry_parms->data.bytes[end_padding_offset], 0, entry_parms->data.len - end_padding_offset); // zerofill |
10 | pmbaty | 1956 | entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF; // mark this inode as a preprocessed ELF file |
1957 | } // end if the file is not yet a processed ELF |
||
9 | pmbaty | 1958 | } // end if the file we're storing is an ELF file |
1 | pmbaty | 1959 | } |
1960 | else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ? |
||
15 | pmbaty | 1961 | LOG_INFO ("symlink: ino 0x%x uid %d gid %d mode 0%o path \"%s\" -> \"%s\"", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.bytes); |
1 | pmbaty | 1962 | else // we must be storing a FIFO |
1963 | { |
||
11 | pmbaty | 1964 | if (strchr (entry_parms->data.bytes, ':') == NULL) |
15 | pmbaty | 1965 | DIE_WITH_EXITCODE (1, "device entry \"%s\" malformed (no 'dev:rdev' pair)", stored_pathname); |
1966 | LOG_INFO ("fifo: ino 0x%x uid %d gid %d mode 0%o path \"%s\" dev:rdev %s)", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.bytes); |
||
1 | pmbaty | 1967 | } |
1968 | |||
7 | pmbaty | 1969 | // grow filesystem entries array to hold one more slot |
1 | pmbaty | 1970 | reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate |
15 | pmbaty | 1971 | WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); |
1 | pmbaty | 1972 | *fsentries = reallocated_ptr; // save reallocated pointer |
1973 | fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot |
||
1974 | //fsentry->header.size = 0; // will be filled once we know it |
||
1975 | fsentry->header.extattr_offset = 0; |
||
10 | pmbaty | 1976 | fsentry->header.ino = entry_parms->extra_ino_flags | (++inode_count); |
1 | pmbaty | 1977 | fsentry->header.mode = entry_parms->st_mode; |
1978 | fsentry->header.gid = entry_parms->gid; |
||
1979 | fsentry->header.uid = entry_parms->uid; |
||
7 | pmbaty | 1980 | fsentry->header.mtime = (entry_parms->mtime == UINT32_MAX ? (uint32_t) time (NULL) : entry_parms->mtime); |
1 | pmbaty | 1981 | if (S_ISDIR (entry_parms->st_mode)) |
1982 | { |
||
7 | pmbaty | 1983 | fsentry->u.dir.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname); |
1984 | fsentry->header.size = (uint16_t) ROUND_TO_UPPER_MULTIPLE (sizeof (fsentry->header) + strlen (fsentry->u.dir.path) + 1, image_align); // now we can set the size |
||
1 | pmbaty | 1985 | fsentry->UNSAVED_was_data_written = true; // no data to save |
1986 | } |
||
1987 | else if (S_ISREG (entry_parms->st_mode)) |
||
1988 | { |
||
1989 | fsentry->u.file.offset = WILL_BE_FILLED_LATER; // will be filled later in main() when the file's data blob will be written to the output file |
||
11 | pmbaty | 1990 | fsentry->u.file.size = (uint32_t) entry_parms->data.len; |
7 | pmbaty | 1991 | fsentry->u.file.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname); |
11 | pmbaty | 1992 | fsentry->u.file.UNSAVED_databuf = malloc (entry_parms->data.len); |
8 | pmbaty | 1993 | WELLMANNERED_ASSERT (fsentry->u.file.UNSAVED_databuf, "out of memory"); |
11 | pmbaty | 1994 | memcpy (fsentry->u.file.UNSAVED_databuf, entry_parms->data.bytes, entry_parms->data.len); |
7 | pmbaty | 1995 | fsentry->header.size = (uint16_t) ROUND_TO_UPPER_MULTIPLE (sizeof (fsentry->header) + sizeof (uint32_t) + sizeof (uint32_t) + strlen (fsentry->u.file.path) + 1, image_align); // now we can set the size |
1 | pmbaty | 1996 | fsentry->UNSAVED_was_data_written = false; // there *IS* data to save |
1997 | } |
||
1998 | else if (S_ISLNK (entry_parms->st_mode)) |
||
1999 | { |
||
7 | pmbaty | 2000 | fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname) + 1); |
11 | pmbaty | 2001 | fsentry->u.symlink.sym_size = (uint16_t) entry_parms->data.len; |
7 | pmbaty | 2002 | fsentry->u.symlink.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname); |
11 | pmbaty | 2003 | fsentry->u.symlink.contents = strdup (entry_parms->data.bytes); |
8 | pmbaty | 2004 | WELLMANNERED_ASSERT (fsentry->u.symlink.contents, "out of memory"); |
7 | pmbaty | 2005 | fsentry->header.size = (uint16_t) ROUND_TO_UPPER_MULTIPLE (sizeof (fsentry->header) + sizeof (uint16_t) + sizeof (uint16_t) + (size_t) fsentry->u.symlink.sym_offset + fsentry->u.symlink.sym_size + 1, image_align); // now we can set the size |
1 | pmbaty | 2006 | fsentry->UNSAVED_was_data_written = true; // no data to save |
2007 | } |
||
7 | pmbaty | 2008 | else // necessarily a device node |
1 | pmbaty | 2009 | { |
11 | pmbaty | 2010 | fsentry->u.device.dev = strtol (entry_parms->data.bytes, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers |
2011 | fsentry->u.device.rdev = strtol (strchr (entry_parms->data.bytes, ':') + 1, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers |
||
7 | pmbaty | 2012 | fsentry->u.device.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname); |
2013 | fsentry->header.size = (uint16_t) ROUND_TO_UPPER_MULTIPLE (sizeof (fsentry->header) + sizeof (uint32_t) + sizeof (uint32_t) + strlen (fsentry->u.device.path), image_align); // now we can set the size |
||
1 | pmbaty | 2014 | fsentry->UNSAVED_was_data_written = true; // no data to save |
2015 | } |
||
2016 | (*fsentry_count)++; |
||
7 | pmbaty | 2017 | |
2018 | // should we also add a symlink to this entry ? (in case we stored a dylib file under its canonical name) |
||
2019 | if (original_stored_pathname != NULL) |
||
2020 | { |
||
2021 | entry_parms->is_compiled_bootscript = false; |
||
2022 | entry_parms->should_autosymlink_dylib = false; |
||
2023 | entry_parms->should_follow_symlinks = false; |
||
2024 | entry_parms->st_mode = S_IFLNK | 0777; // NOTE: mkifs stores symlink permissions as rwxrwxrwx ! |
||
10 | pmbaty | 2025 | entry_parms->extra_ino_flags = (fsentry->header.ino & (IFS_INO_PROCESSED_ELF | IFS_INO_RUNONCE_ELF | IFS_INO_BOOTSTRAP_EXE)); // preserve target's inode flags |
7 | pmbaty | 2026 | last_dirsep = strrchr (stored_pathname, '/'); |
11 | pmbaty | 2027 | old_data = entry_parms->data.bytes; // backup previous data pointer |
2028 | entry_parms->data.bytes = (uint8_t *) (last_dirsep == NULL ? stored_pathname : last_dirsep + 1); // store symlink target in dirent data |
||
2029 | entry_parms->data.len = strlen (entry_parms->data.bytes); |
||
7 | pmbaty | 2030 | add_fsentry (fsentries, fsentry_count, entry_parms, original_stored_pathname, NULL); |
11 | pmbaty | 2031 | entry_parms->data.bytes = old_data; // restore previous data pointer so that it can be freed normally |
7 | pmbaty | 2032 | } |
2033 | |||
1 | pmbaty | 2034 | return (*fsentry_count); |
2035 | } |
||
2036 | |||
2037 | |||
2038 | static int fsentry_compare_pathnames_cb (const void *a, const void *b) |
||
2039 | { |
||
2040 | // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname |
||
2041 | |||
3 | pmbaty | 2042 | const fsentry_t *entry_a = (const fsentry_t *) a; |
2043 | const fsentry_t *entry_b = (const fsentry_t *) b; |
||
1 | pmbaty | 2044 | const char *pathname_a = (S_ISDIR (entry_a->header.mode) ? entry_a->u.dir.path : (S_ISREG (entry_a->header.mode) ? entry_a->u.file.path : (S_ISLNK (entry_a->header.mode) ? entry_a->u.symlink.path : entry_a->u.device.path))); |
2045 | const char *pathname_b = (S_ISDIR (entry_b->header.mode) ? entry_b->u.dir.path : (S_ISREG (entry_b->header.mode) ? entry_b->u.file.path : (S_ISLNK (entry_b->header.mode) ? entry_b->u.symlink.path : entry_b->u.device.path))); |
||
2046 | return (strcmp (pathname_a, pathname_b)); |
||
2047 | } |
||
2048 | |||
2049 | |||
7 | pmbaty | 2050 | static void update_MKIFS_PATH (const char *processor) |
1 | pmbaty | 2051 | { |
7 | pmbaty | 2052 | // updates the value of MKIFS_PATH according to the passed processor name string, unless an environment variable already defines it |
1 | pmbaty | 2053 | |
7 | pmbaty | 2054 | char processor_base[16]; |
2055 | size_t data_len; |
||
2056 | char *envvar; |
||
2057 | char *token; |
||
2058 | |||
2059 | envvar = getenv ("MKIFS_PATH"); // look in the environment first, and construct a default one if not supplied |
||
2060 | if (envvar != NULL) |
||
2061 | MKIFS_PATH = envvar; // if envvar is present, set MKIFS_PATH to point to it |
||
2062 | else // envvar not present |
||
2063 | { |
||
2064 | if (MKIFS_PATH != NULL) |
||
2065 | free (MKIFS_PATH); // free any MKIFS_PATH that we constructed earlier |
||
2066 | |||
2067 | strcpy (processor_base, processor); // construct PROCESSOR_BASE |
||
2068 | token = strchr (processor_base, '-'); |
||
2069 | if (token != NULL) |
||
2070 | *token = 0; // split anything from the first dash onwards |
||
2071 | data_len = strlen (processor_base); |
||
2072 | if ((data_len > 2) && ((processor_base[data_len - 2] == 'b') || (processor_base[data_len - 2] == 'l')) && (processor_base[data_len - 1] == 'e')) |
||
2073 | processor_base[data_len - 2] = 0; // if it ends with "le" or "be", strip that too |
||
2074 | |||
2075 | MKIFS_PATH = malloc (10 * MAXPATHLEN); // construct a default MKIFS_PATH now |
||
8 | pmbaty | 2076 | WELLMANNERED_ASSERT (MKIFS_PATH, "out of memory"); |
7 | pmbaty | 2077 | sprintf (MKIFS_PATH, "." PATH_SEP_STR "%s/%s/sbin" PATH_SEP_STR "%s/%s/usr/sbin" PATH_SEP_STR "%s/%s/boot/sys" PATH_SEP_STR "%s/%s/boot/sys" PATH_SEP_STR "%s/%s/bin" PATH_SEP_STR "%s/%s/usr/bin" PATH_SEP_STR "%s/%s/lib" PATH_SEP_STR "%s/%s/lib/dll" PATH_SEP_STR "%s/%s/usr/lib", // use a platform-specific character as path separator |
2078 | QNX_TARGET, processor, |
||
2079 | QNX_TARGET, processor, |
||
2080 | QNX_TARGET, processor, |
||
2081 | QNX_TARGET, processor_base, |
||
2082 | QNX_TARGET, processor, |
||
2083 | QNX_TARGET, processor, |
||
2084 | QNX_TARGET, processor, |
||
2085 | QNX_TARGET, processor, |
||
2086 | QNX_TARGET, processor); |
||
2087 | } |
||
2088 | |||
2089 | return; |
||
1 | pmbaty | 2090 | } |
2091 | |||
2092 | |||
2093 | int main (int argc, char **argv) |
||
2094 | { |
||
2095 | // program entrypoint |
||
2096 | |||
2097 | #define PAD_OUTFILE_TO(val) do { curr_offset = ftell (fp); while (curr_offset < (val)) { putc (0, fp); curr_offset++; } } while (0) |
||
2098 | |||
2 | pmbaty | 2099 | static startup_header_t startup_header = { 0 }; // output IFS's startup header |
2100 | static startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum) |
||
2101 | static image_header_t image_header = { 0 }; // output IFS's imagefs header |
||
2102 | static image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum) |
||
2103 | static fsentry_t *fsentries = NULL; // output IFS's filesystem entries |
||
2104 | static size_t fsentry_count = 0; // number of entries in the IFS filesystem |
||
7 | pmbaty | 2105 | static parms_t default_parms = { // default parameters for a filesystem entry |
2106 | .dperms = 0755, |
||
2107 | .perms = 0644, |
||
2108 | .uid = 0, |
||
2109 | .gid = 0, |
||
2110 | .st_mode = S_IFREG, |
||
2111 | .mtime = UINT32_MAX, |
||
2112 | .mtime_for_inline_files = UINT32_MAX, |
||
2113 | .prefix = "/proc/boot", |
||
2114 | .should_follow_symlinks = true, // [+|-followlink] |
||
2115 | .should_autosymlink_dylib = true, // [+|-autolink] |
||
2116 | .is_compiled_bootscript = false, // [+|-script] |
||
10 | pmbaty | 2117 | .extra_ino_flags = 0, |
7 | pmbaty | 2118 | .search = "", |
11 | pmbaty | 2119 | .data = { NULL, 0 } |
7 | pmbaty | 2120 | }; |
2 | pmbaty | 2121 | static parms_t entry_parms = { 0 }; // current parameters for a filesystem entry (will be initialized to default_parms each time a new entry is parsed in the build file) |
1 | pmbaty | 2122 | |
7 | pmbaty | 2123 | char path_on_buildhost[MAXPATHLEN] = ""; |
2124 | char path_in_ifs[MAXPATHLEN] = ""; |
||
1 | pmbaty | 2125 | char *ifs_pathname = NULL; |
2126 | void *reallocated_ptr; |
||
10 | pmbaty | 2127 | const elf_header_t *elf; |
2128 | elf_program_header_t *phdr; |
||
7 | pmbaty | 2129 | struct tm utc_time; |
1 | pmbaty | 2130 | struct stat stat_buf; |
2131 | size_t startuptrailer_offset; |
||
2132 | size_t startupheader_offset; |
||
8 | pmbaty | 2133 | size_t imagetrailer_offset; |
2134 | size_t imageheader_offset; |
||
11 | pmbaty | 2135 | size_t corrective_offset; |
1 | pmbaty | 2136 | size_t imgdir_offset; |
2137 | size_t imgdir_size; |
||
2138 | size_t final_size; |
||
7 | pmbaty | 2139 | size_t available_space; |
2140 | size_t allocated_size; |
||
1 | pmbaty | 2141 | size_t fsentry_index; |
7 | pmbaty | 2142 | size_t largest_index; |
2143 | size_t largest_size; |
||
1 | pmbaty | 2144 | size_t curr_offset; |
10 | pmbaty | 2145 | size_t table_index; |
2146 | size_t table_count; |
||
11 | pmbaty | 2147 | buffer_t blob; |
8 | pmbaty | 2148 | int32_t checksum; |
7 | pmbaty | 2149 | char *specifiedpathname_start; |
1 | pmbaty | 2150 | char *directiveblock_start; |
2151 | char *write_ptr; |
||
7 | pmbaty | 2152 | char *line_ptr; |
14 | pmbaty | 2153 | char *outdir = "."; |
1 | pmbaty | 2154 | char *token; |
2155 | char *value; |
||
2156 | char *sep; |
||
2157 | //char *ctx; |
||
2158 | int arg_index; |
||
2159 | bool is_quoted_context = false; |
||
2160 | bool is_escaped_char = false; |
||
11 | pmbaty | 2161 | bool should_discard_inline_contents = false; |
1 | pmbaty | 2162 | bool want_info = false; |
10 | pmbaty | 2163 | bool want_everything = false; |
1 | pmbaty | 2164 | bool want_help = false; |
14 | pmbaty | 2165 | bool want_dump = false; |
15 | pmbaty | 2166 | bool want_hexdump = false; |
5 | pmbaty | 2167 | bool is_foreign_endianness; |
1 | pmbaty | 2168 | int string_len; |
2169 | int read_char; |
||
2170 | FILE *buildfile_fp; |
||
2171 | FILE *fp; |
||
2172 | |||
2173 | // parse arguments |
||
2174 | for (arg_index = 1; arg_index < argc; arg_index++) |
||
2175 | { |
||
2176 | if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin |
||
2177 | bootfile_pathname = argv[++arg_index]; |
||
2178 | else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030 |
||
2179 | { |
||
2180 | sep = strchr (argv[++arg_index], '@'); |
||
2181 | if ((sep == NULL) || (sep[1] == 0)) |
||
15 | pmbaty | 2182 | DIE_WITH_EXITCODE (1, "the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>"); |
1 | pmbaty | 2183 | *sep = 0; |
2184 | startupfile_pathname = argv[arg_index]; |
||
2185 | startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1); |
||
2186 | } |
||
2187 | else if ((strcmp (argv[arg_index], "--kernelfile") == 0) && (arg_index + 1 < argc)) // --kernelfile path/to/blob.bin@0x32000 |
||
2188 | { |
||
2189 | sep = strchr (argv[++arg_index], '@'); |
||
2190 | if ((sep == NULL) || (sep[1] == 0)) |
||
15 | pmbaty | 2191 | DIE_WITH_EXITCODE (1, "the --kernelfile arguments expects <pathname>@<fileoffset>"); |
1 | pmbaty | 2192 | *sep = 0; |
2193 | kernelfile_pathname = argv[arg_index]; |
||
2194 | kernelfile_offset = (size_t) read_integer (sep + 1); |
||
2195 | } |
||
7 | pmbaty | 2196 | else if (strcmp (argv[arg_index], "-n") == 0) |
2197 | default_parms.mtime_for_inline_files = 0; // inline files should have a mtime set to zero |
||
2198 | else if (strcmp (argv[arg_index], "-nn") == 0) |
||
2199 | { |
||
11 | pmbaty | 2200 | default_parms.mtime = 0; // *all* files should have a mtime set to zero |
7 | pmbaty | 2201 | default_parms.mtime_for_inline_files = 0; |
2202 | } |
||
14 | pmbaty | 2203 | else if ((strcmp (argv[arg_index], "--outdir") == 0) && (arg_index + 1 < argc)) // --outdir path |
2204 | outdir = argv[++arg_index]; |
||
1 | pmbaty | 2205 | else if (strcmp (argv[arg_index], "--info") == 0) |
2206 | want_info = true; |
||
14 | pmbaty | 2207 | else if (strcmp (argv[arg_index], "--dump") == 0) |
2208 | want_dump = true; |
||
15 | pmbaty | 2209 | else if (strcmp (argv[arg_index], "--hexdump") == 0) // voluntarily undocumented |
2210 | want_hexdump = true; |
||
10 | pmbaty | 2211 | else if (strcmp (argv[arg_index], "--everything") == 0) |
2212 | want_everything = true; |
||
15 | pmbaty | 2213 | else if (strncmp (argv[arg_index], "-v", 2) == 0) // -v[....] |
2214 | verbose_level += (int) strlen (argv[arg_index] + 1); // increase verbosity by the number of characters in this flag |
||
1 | pmbaty | 2215 | else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0)) |
2216 | want_help = true; |
||
2217 | else if (buildfile_pathname == NULL) |
||
2218 | buildfile_pathname = argv[arg_index]; |
||
2219 | else if (ifs_pathname == NULL) |
||
2220 | ifs_pathname = argv[arg_index]; |
||
2221 | } |
||
2222 | |||
2223 | // do we not have enough information to run ? |
||
15 | pmbaty | 2224 | if (want_help || (buildfile_pathname == NULL) || (!want_info && !want_dump && !want_hexdump && (ifs_pathname == NULL))) |
1 | pmbaty | 2225 | { |
12 | pmbaty | 2226 | fp = (want_help ? stdout : stderr); // select the right output channel |
2227 | fprintf (fp, "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n"); |
||
2228 | fprintf (fp, " version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD); |
||
4 | pmbaty | 2229 | if (!want_help) |
12 | pmbaty | 2230 | fprintf (fp, "error: missing parameters\n"); |
2231 | fprintf (fp, "usage:\n"); |
||
2232 | fprintf (fp, " ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kernelfile <pathname>@<fileoffs>] [-n[n]] <buildfile> <outfile>\n"); |
||
2233 | fprintf (fp, " ifstool --info [--everything] <ifs file>\n"); |
||
14 | pmbaty | 2234 | fprintf (fp, " ifstool --dump [--outdir <path>] <ifs file>\n"); |
12 | pmbaty | 2235 | fprintf (fp, " ifstool --help\n"); |
2236 | fprintf (fp, "NOTE: the compilation feature requires predigested boot, startup and kernel files produced by mkifs.\n"); |
||
1 | pmbaty | 2237 | exit (want_help ? 0 : 1); |
2238 | } |
||
2239 | |||
14 | pmbaty | 2240 | // do we want info about a particular IFS ? if so, dissecate it |
2 | pmbaty | 2241 | if (want_info) |
10 | pmbaty | 2242 | exit (dump_ifs_info (buildfile_pathname, want_everything)); // NOTE: the first argument after --info is actually the IFS file, not a build file, but the arguments are collected in this order |
2 | pmbaty | 2243 | |
14 | pmbaty | 2244 | // else do we want to dump its contents ? if so, do so |
2245 | else if (want_dump) |
||
2246 | exit (dump_ifs_contents (buildfile_pathname, outdir)); // NOTE: the first argument after --info is actually the IFS file, not a build file, but the arguments are collected in this order |
||
2247 | |||
15 | pmbaty | 2248 | // else do we want to hex dump a file ? (this is voluntarily undocumented) |
2249 | else if (want_hexdump) |
||
2250 | { |
||
2251 | if (read_filecontents (buildfile_pathname, ".", &blob) == NULL) |
||
2252 | DIE_WITH_EXITCODE (1, "can't read \"%s\": %s", buildfile_pathname, strerror (errno)); |
||
2253 | hex_fprintf (stdout, blob.bytes, blob.len, 16, "%s (%zd bytes):\n", buildfile_pathname, blob.len); |
||
2254 | exit (0); |
||
2255 | } |
||
2256 | |||
1 | pmbaty | 2257 | // make sure we have ${QNX_TARGET} pointing somewhere |
2258 | QNX_TARGET = getenv ("QNX_TARGET"); |
||
2259 | if (QNX_TARGET == NULL) |
||
15 | pmbaty | 2260 | DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable is not set"); |
1 | pmbaty | 2261 | else if (access (QNX_TARGET, 0) != 0) |
15 | pmbaty | 2262 | DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable doesn't point to an existing directory"); |
1 | pmbaty | 2263 | |
7 | pmbaty | 2264 | // prepare a default MKIFS_PATH assuming the host processor |
2265 | update_MKIFS_PATH (image_processor); |
||
2266 | |||
1 | pmbaty | 2267 | // open build file |
2268 | buildfile_fp = fopen (buildfile_pathname, "rb"); |
||
2269 | if (buildfile_fp == NULL) |
||
15 | pmbaty | 2270 | DIE_WITH_EXITCODE (1, "unable to open build file \"%s\" for reading: %s", buildfile_pathname, strerror (errno)); |
1 | pmbaty | 2271 | |
2272 | // stack up filesystem entries |
||
2273 | memcpy (&entry_parms, &default_parms, sizeof (default_parms)); |
||
2274 | entry_parms.st_mode = S_IFDIR | default_parms.dperms; |
||
7 | pmbaty | 2275 | add_fsentry (&fsentries, &fsentry_count, &entry_parms, "", NULL); // add the root dir first |
1 | pmbaty | 2276 | |
11 | pmbaty | 2277 | // parse the IFS build file line per line |
1 | pmbaty | 2278 | while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL) |
2279 | { |
||
7 | pmbaty | 2280 | if (current_line != NULL) |
2281 | free (current_line); |
||
2282 | current_line = strdup (line_buffer); |
||
8 | pmbaty | 2283 | WELLMANNERED_ASSERT (current_line, "out of memory"); |
1 | pmbaty | 2284 | lineno++; // keep track of current line number |
2285 | |||
2286 | line_ptr = line_buffer; |
||
2287 | while ((*line_ptr != 0) && isspace (*line_ptr)) |
||
2288 | line_ptr++; // skip leading spaces |
||
2289 | |||
2290 | if ((*line_ptr == 0) || (*line_ptr == '#')) |
||
2291 | continue; // skip empty or comment lines |
||
2292 | |||
2293 | string_len = (int) strlen (line_buffer); |
||
2294 | if ((string_len > 0) && (line_buffer[string_len - 1] == '\n')) |
||
2295 | line_buffer[string_len - 1] = 0; // chop off newline for easier debug output |
||
2296 | |||
2297 | // reset entry values |
||
2298 | memcpy (&entry_parms, &default_parms, sizeof (default_parms)); |
||
7 | pmbaty | 2299 | path_in_ifs[0] = 0; |
2300 | path_on_buildhost[0] = 0; |
||
11 | pmbaty | 2301 | should_discard_inline_contents = false; |
1 | pmbaty | 2302 | |
2303 | // does this line start with an attribute block ? |
||
2304 | if (*line_ptr == '[') |
||
2305 | { |
||
2306 | line_ptr++; // skip the leading square bracket |
||
2307 | directiveblock_start = line_ptr; // remember where it starts |
||
2308 | is_quoted_context = false; |
||
15 | pmbaty | 2309 | while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\') && !is_quoted_context)) |
1 | pmbaty | 2310 | { |
2311 | if (*line_ptr == '"') |
||
2312 | is_quoted_context ^= true; // remember when we're between quotes |
||
2313 | else if (!is_quoted_context && (*line_ptr == ' ')) |
||
2314 | *line_ptr = RECORD_SEP; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting |
||
2315 | line_ptr++; // reach the next unescaped closing square bracket |
||
2316 | } |
||
2317 | if (*line_ptr != ']') |
||
2318 | { |
||
15 | pmbaty | 2319 | LOG ("warning", 0, "syntax error in \"%s\" line %d: unterminated attributes block (skipping)", buildfile_pathname, lineno); |
1 | pmbaty | 2320 | continue; // invalid attribute block, skip line |
2321 | } |
||
2322 | *line_ptr = 0; // end the attribute block so that it is a parsable C string |
||
2323 | |||
2324 | // now parse the attribute tokens |
||
2325 | // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description |
||
2326 | token = strtok (directiveblock_start, RECORD_SEP_STR); |
||
2327 | while (token != NULL) |
||
2328 | { |
||
2329 | // evaluate attribute token |
||
2330 | #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0) |
||
2331 | if (strncmp (token, "uid=", 4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid = (int) read_integer (value); } |
||
2332 | else if (strncmp (token, "gid=", 4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid = (int) read_integer (value); } |
||
2333 | else if (strncmp (token, "dperms=", 7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms = (int) read_integer (value); } |
||
2334 | else if (strncmp (token, "perms=", 6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms = (int) read_integer (value); } |
||
15 | pmbaty | 2335 | else if (strncmp (token, "type=", 5) == 0) { REACH_TOKEN_VALUE (); |
2336 | if (strcmp (value, "dir") == 0) entry_parms.st_mode = S_IFDIR; |
||
2337 | else if (strcmp (value, "file") == 0) entry_parms.st_mode = S_IFREG; |
||
2338 | else if (strcmp (value, "link") == 0) entry_parms.st_mode = S_IFLNK; |
||
2339 | else if (strcmp (value, "fifo") == 0) entry_parms.st_mode = S_IFIFO; |
||
2340 | else DIE_WITH_EXITCODE (1, "invalid 'type' attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, value); |
||
2341 | } |
||
1 | pmbaty | 2342 | else if (strncmp (token, "prefix=", 7) == 0) { REACH_TOKEN_VALUE (); strcpy (entry_parms.prefix, (*value == '/' ? value + 1 : value)); } // skip possible leading slash in prefix |
2343 | else if (strncmp (token, "image=", 6) == 0) { REACH_TOKEN_VALUE (); |
||
2344 | image_base = (uint32_t) read_integer (value); // read image base address |
||
12 | pmbaty | 2345 | if ((sep = strchr (value, '-')) != NULL) image_end = (uint32_t) read_integer (sep + 1); // if we have a dash, read optional image end (TODO: check this value and produce an error in the relevant case. Not important.) |
1 | pmbaty | 2346 | if ((sep = strchr (value, ',')) != NULL) image_maxsize = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size |
2347 | if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size |
||
2348 | if ((sep = strchr (value, '%')) != NULL) image_align = (uint32_t) read_integer (sep + 1); // if we have a modulo sign, read optional image aligmnent |
||
15 | pmbaty | 2349 | LOG_INFO ("image 0x%x-0x%x maxsize %d totalsize %d align %d", image_base, image_end, image_maxsize, image_totalsize, image_align); |
1 | pmbaty | 2350 | } |
2351 | else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE (); |
||
2352 | if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL) || (kernelfile_pathname == NULL)) // HACK until I figure out how to re-create them |
||
15 | pmbaty | 2353 | DIE_WITH_EXITCODE (1, "creating bootable images require the --bootfile, --startupfile and --kernelfile command-line options in \"%s\" line %d", buildfile_pathname, lineno); |
1 | pmbaty | 2354 | if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ? |
2355 | { |
||
2356 | *sep = 0; |
||
2357 | strcpy (image_processor, value); // save processor |
||
7 | pmbaty | 2358 | update_MKIFS_PATH (image_processor); |
1 | pmbaty | 2359 | value = sep + 1; |
2360 | } |
||
12 | pmbaty | 2361 | //sprintf (image_bootfile, "%s/%s/boot/sys/%s.boot", QNX_TARGET, image_processor, value); // save preboot file name (TODO: we should search in MKIFS_PATH instead of this. Not important.) |
2 | pmbaty | 2362 | //strcpy (image_bootfile, bootfile_pathname); // FIXME: HACK |
2363 | if (stat (bootfile_pathname, &stat_buf) != 0) |
||
15 | pmbaty | 2364 | DIE_WITH_EXITCODE (1, "unable to stat the boot file \"%s\" specified in \"%s\" line %d: %s", bootfile_pathname, buildfile_pathname, lineno, strerror (errno)); |
2 | pmbaty | 2365 | bootfile_size = stat_buf.st_size; // save preboot file size |
15 | pmbaty | 2366 | LOG_INFO ("processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname); |
11 | pmbaty | 2367 | #if 1 |
2368 | // ###################################################################################################################################################################################################################################### |
||
2369 | // # FIXME: figure out how to re-create it: linker call involved |
||
2370 | // # $ x86_64-pc-nto-qnx8.0.0-ld --sysroot=${QNX_TARGET}/x86_64/ -T${QNX_TARGET}/x86_64/lib/nto.link --section-start .text=0xffff800000001000 --no-relax ${QNX_TARGET}/x86_64/boot/sys/procnto-smp-instr -o procnto-smp-instr.sym.UNSTRIPPED |
||
2371 | // ###################################################################################################################################################################################################################################### |
||
15 | pmbaty | 2372 | // if (read_filecontents (kernelfile_pathname, ".", &entry_parms.data) == NULL) |
2373 | // DIE_WITH_EXITCODE (1, "unable to read precompiled kernel file \"%s\" specified in --kernelfile argument: %s", kernelfile_pathname, strerror (errno)); |
||
11 | pmbaty | 2374 | #else // nonworking |
2375 | strcpy (path_on_buildhost, "procnto-smp-instr"); |
||
2376 | #endif // nonworking |
||
1 | pmbaty | 2377 | } |
7 | pmbaty | 2378 | else if (strncmp (token, "mtime=", 6) == 0) { REACH_TOKEN_VALUE (); if (strcmp (value, "*") == 0) entry_parms.mtime = UINT32_MAX; else { |
2379 | // value *must* be "YYYY-MM-DD-HH:MM:SS" by specification |
||
2380 | memset (&utc_time, 0, sizeof (utc_time)); |
||
2381 | if (sscanf (value, "%u-%u-%u-%u:%u:%u", &utc_time.tm_year, &utc_time.tm_mon, &utc_time.tm_mday, &utc_time.tm_hour, &utc_time.tm_min, &utc_time.tm_sec) != 6) |
||
2382 | { |
||
15 | pmbaty | 2383 | LOG_WARNING ("syntax error in \"%s\" line %d: mtime specification not in YYYY-MM-DD-HH:MM:SS format (skipping)", buildfile_pathname, lineno); |
7 | pmbaty | 2384 | continue; // invalid attribute block, skip line |
2385 | } |
||
2386 | utc_time.tm_mon--; // convert month from [1-12] to [0-11] |
||
2387 | entry_parms.mtime = (uint32_t) mktime (&utc_time); |
||
2388 | } |
||
2389 | } |
||
2390 | else if (strcmp (token, "+script") == 0) { |
||
2 | pmbaty | 2391 | entry_parms.is_compiled_bootscript = true; |
11 | pmbaty | 2392 | entry_parms.data.bytes = malloc (sizeof (INITIAL_STARTUP_SCRIPT) - 1); |
2393 | WELLMANNERED_ASSERT (entry_parms.data.bytes, "out of memory"); |
||
2394 | memcpy (entry_parms.data.bytes, INITIAL_STARTUP_SCRIPT, sizeof (INITIAL_STARTUP_SCRIPT) - 1); // FIXME: HACK until the script compiler is implemented |
||
2395 | entry_parms.data.len = sizeof (INITIAL_STARTUP_SCRIPT) - 1; |
||
2396 | should_discard_inline_contents = true; // remember we already have data (so as to discard the inline block's contents) |
||
2 | pmbaty | 2397 | } |
7 | pmbaty | 2398 | else if (strcmp (token, "-script") == 0) entry_parms.is_compiled_bootscript = false; |
2399 | else if (strcmp (token, "+followlink") == 0) entry_parms.should_follow_symlinks = true; |
||
2400 | else if (strcmp (token, "-followlink") == 0) entry_parms.should_follow_symlinks = false; |
||
2401 | else if (strcmp (token, "+autolink") == 0) entry_parms.should_autosymlink_dylib = true; |
||
2402 | else if (strcmp (token, "-autolink") == 0) entry_parms.should_autosymlink_dylib = false; |
||
15 | pmbaty | 2403 | else if (strcmp (token, "+keeplinked") == 0) entry_parms.should_keep_ld_output = true; |
2404 | else if (strcmp (token, "-keeplinked") == 0) entry_parms.should_keep_ld_output = false; |
||
2405 | else LOG_WARNING ("unimplemented attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, token); |
||
1 | pmbaty | 2406 | #undef REACH_TOKEN_VALUE |
2407 | |||
2408 | token = strtok (NULL, RECORD_SEP_STR); // proceed to next attribute token |
||
2409 | } |
||
2410 | |||
2411 | line_ptr++; // reach the next character |
||
2412 | while ((*line_ptr != 0) && isspace (*line_ptr)) |
||
2413 | line_ptr++; // skip leading spaces |
||
2414 | |||
2415 | // are we at the end of the line ? if so, it means the attribute values that are set should become the default |
||
2416 | if ((*line_ptr == 0) || (*line_ptr == '#')) |
||
2417 | { |
||
2418 | #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms.attr) { \ |
||
15 | pmbaty | 2419 | LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d", default_parms.attr, entry_parms.attr, buildfile_pathname, lineno); \ |
1 | pmbaty | 2420 | default_parms.attr = entry_parms.attr; \ |
2421 | } } while (0) |
||
2422 | #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (strcmp (entry_parms.attr, default_parms.attr) != 0) { \ |
||
15 | pmbaty | 2423 | LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d", default_parms.attr, entry_parms.attr, buildfile_pathname, lineno); \ |
1 | pmbaty | 2424 | strcpy (default_parms.attr, entry_parms.attr); \ |
2425 | } } while (0) |
||
7 | pmbaty | 2426 | APPLY_DEFAULT_ATTR_NUM (dperms, "directory permissions", "0%o"); |
2427 | APPLY_DEFAULT_ATTR_NUM (perms, "file permissions", "0%o"); |
||
2428 | APPLY_DEFAULT_ATTR_NUM (uid, "owner ID", "%d"); |
||
2429 | APPLY_DEFAULT_ATTR_NUM (gid, "group ID", "%d"); |
||
2430 | APPLY_DEFAULT_ATTR_NUM (st_mode, "inode type", "0%o"); |
||
2431 | APPLY_DEFAULT_ATTR_STR (prefix, "prefix", "\"%s\""); |
||
2432 | APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript, "compiled script state", "%d"); |
||
2433 | APPLY_DEFAULT_ATTR_NUM (should_follow_symlinks, "symlink resolution", "%d"); |
||
2434 | APPLY_DEFAULT_ATTR_NUM (should_autosymlink_dylib, "dylib canonical name symlinking", "%d"); |
||
15 | pmbaty | 2435 | APPLY_DEFAULT_ATTR_NUM (should_keep_ld_output, "linker output preservation", "%d"); |
1 | pmbaty | 2436 | #undef APPLY_DEFAULT_ATTR_STR |
2437 | #undef APPLY_DEFAULT_ATTR_NUM |
||
2438 | continue; // end of line reached, proceed to the next line |
||
2439 | } |
||
2440 | // end of attributes parsing |
||
2441 | } // end of "this line starts with an attributes block" |
||
2442 | |||
2443 | // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters |
||
2444 | string_len = sprintf (path_in_ifs, "%s", entry_parms.prefix); |
||
2445 | while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/')) |
||
2446 | string_len--; // chop off any trailing slashes from prefix |
||
2447 | write_ptr = &path_in_ifs[string_len]; |
||
2448 | *write_ptr++ = '/'; // add ONE trailing slash |
||
7 | pmbaty | 2449 | specifiedpathname_start = write_ptr; // remember the specified pathname will start here |
2450 | is_quoted_context = (*line_ptr == '"'); |
||
2451 | if (is_quoted_context) |
||
2452 | line_ptr++; // skip a possible initial quote |
||
1 | pmbaty | 2453 | if (*line_ptr == '/') |
2454 | { |
||
15 | pmbaty | 2455 | LOG_WARNING ("paths in the IFS file should not begin with a leading '/' in \"%s\" line %d", buildfile_pathname, lineno); |
1 | pmbaty | 2456 | line_ptr++; // consistency check: paths in the IFS should not begin with a '/' |
2457 | } |
||
7 | pmbaty | 2458 | while ((*line_ptr != 0) && ((!is_quoted_context && (*line_ptr != '=') && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"')))) |
1 | pmbaty | 2459 | { |
2460 | if (*line_ptr == '\\') |
||
2461 | { |
||
2462 | line_ptr++; |
||
2463 | *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\' |
||
2464 | } |
||
2465 | else |
||
2466 | *write_ptr++ = *line_ptr; |
||
2467 | line_ptr++; |
||
2468 | } |
||
2469 | *write_ptr = 0; // terminate the string |
||
7 | pmbaty | 2470 | if (is_quoted_context && (*line_ptr == '"')) |
2471 | line_ptr++; // skip a possible final quote |
||
1 | pmbaty | 2472 | |
2473 | // we reached a space OR an equal sign |
||
2474 | while ((*line_ptr != 0) && isspace (*line_ptr)) |
||
2475 | line_ptr++; // skip optional spaces after the filename in the IFS |
||
2476 | |||
2477 | // do we have an equal sign ? |
||
2478 | if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ? |
||
2479 | { |
||
2480 | line_ptr++; // skip the equal sign |
||
2481 | while ((*line_ptr != 0) && isspace (*line_ptr)) |
||
2482 | line_ptr++; // skip optional spaces after the equal sign |
||
2483 | |||
2484 | if (*line_ptr == 0) |
||
2485 | { |
||
15 | pmbaty | 2486 | LOG_WARNING ("syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)", buildfile_pathname, lineno); |
1 | pmbaty | 2487 | continue; // invalid symlink specification, skip line |
2488 | } |
||
2489 | |||
2490 | // read the host system's path, it may be either a path or a contents definition. Is it a content definition ? |
||
2491 | if (*line_ptr == '{') |
||
2492 | { |
||
7 | pmbaty | 2493 | allocated_size = 0; |
2494 | |||
1 | pmbaty | 2495 | line_ptr++; // skip the leading content definition |
2496 | is_escaped_char = false; |
||
2497 | for (;;) |
||
2498 | { |
||
2499 | read_char = fgetc (buildfile_fp); |
||
2500 | if (read_char == EOF) |
||
15 | pmbaty | 2501 | DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %d: unterminated contents block (end of file reached)", buildfile_pathname, lineno); // invalid contents block |
8 | pmbaty | 2502 | else if ((read_char == '\\') && !is_escaped_char) |
1 | pmbaty | 2503 | is_escaped_char = true; // remember the next char is escaped |
2504 | else if ((read_char == '}') && !is_escaped_char) |
||
2505 | break; // found an unescaped closing bracked, stop parsing |
||
2506 | else |
||
2507 | { |
||
2508 | is_escaped_char = false; // any other char, meaning the next one will not be escaped |
||
11 | pmbaty | 2509 | if (!should_discard_inline_contents) // only store the contents if we do NOT know the data yet |
1 | pmbaty | 2510 | { |
11 | pmbaty | 2511 | if (entry_parms.data.len == allocated_size) // reallocate in 4 kb blocks |
1 | pmbaty | 2512 | { |
11 | pmbaty | 2513 | reallocated_ptr = realloc (entry_parms.data.bytes, allocated_size + 4096); |
2514 | WELLMANNERED_ASSERT (reallocated_ptr != NULL, "out of memory"); |
||
2515 | entry_parms.data.bytes = reallocated_ptr; |
||
7 | pmbaty | 2516 | allocated_size += 4096; |
1 | pmbaty | 2517 | } |
11 | pmbaty | 2518 | entry_parms.data.bytes[entry_parms.data.len++] = read_char; |
1 | pmbaty | 2519 | } |
2520 | if (read_char == '\n') |
||
8 | pmbaty | 2521 | lineno++; // update line counter as we parse the inline content |
1 | pmbaty | 2522 | } |
8 | pmbaty | 2523 | } // end for |
1 | pmbaty | 2524 | } |
7 | pmbaty | 2525 | else // not a content definition between { brackets }, must be either a pathname on the build host, or the target of a symlink |
1 | pmbaty | 2526 | { |
7 | pmbaty | 2527 | is_quoted_context = (*line_ptr == '"'); |
2528 | if (is_quoted_context) |
||
2529 | line_ptr++; // skip a possible initial quote |
||
2530 | specifiedpathname_start = line_ptr; // remember where the specified pathname starts |
||
2531 | write_ptr = line_ptr; // now unescape all characters |
||
2532 | while ((*line_ptr != 0) && ((!is_quoted_context && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"')))) |
||
2533 | { |
||
2534 | if (*line_ptr == '\\') |
||
2535 | { |
||
2536 | line_ptr++; |
||
2537 | *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\' |
||
2538 | } |
||
2539 | else |
||
2540 | *write_ptr++ = *line_ptr; |
||
2541 | line_ptr++; |
||
2542 | } |
||
2543 | *write_ptr = 0; // terminate the string |
||
2544 | if (is_quoted_context && (*line_ptr == '"')) |
||
2545 | line_ptr++; // skip a possible final quote |
||
2546 | |||
2547 | if (S_ISLNK (entry_parms.st_mode)) // are we storing a symlink ? |
||
2548 | { |
||
11 | pmbaty | 2549 | entry_parms.data.bytes = strdup (specifiedpathname_start); // if so, store the symlink target as the dirent's blob data |
2550 | WELLMANNERED_ASSERT (entry_parms.data.bytes != NULL, "out of memory"); |
||
2551 | entry_parms.data.len = strlen (specifiedpathname_start); |
||
7 | pmbaty | 2552 | } |
2553 | else // it's a build host filesystem path |
||
2554 | strcpy (path_on_buildhost, line_ptr); // the path on the build host is given after the equal sign |
||
1 | pmbaty | 2555 | } |
2556 | } |
||
2557 | else // no equal sign, meaning the file will have the same name on the build host filesystem |
||
2558 | { |
||
2559 | // consistency check: symlinks MUST have an equal sign |
||
2560 | if (entry_parms.st_mode == S_IFLNK) |
||
2561 | { |
||
15 | pmbaty | 2562 | LOG_WARNING ("syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)", buildfile_pathname, lineno); |
1 | pmbaty | 2563 | continue; // invalid symlink specification, skip line |
2564 | } |
||
2565 | |||
7 | pmbaty | 2566 | strcpy (path_on_buildhost, specifiedpathname_start); // the path on the build host is the one specified |
2567 | sep = strrchr (specifiedpathname_start, '/'); |
||
2568 | if (sep != NULL) |
||
2569 | memmove (specifiedpathname_start, sep + 1, strlen (sep + 1) + 1); // the path in the IFS will be the BASENAME of the path specified (after the prefix) |
||
1 | pmbaty | 2570 | } |
2571 | |||
2572 | // now add this entry to the image filesystem |
||
7 | pmbaty | 2573 | if (S_ISDIR (entry_parms.st_mode)) |
2574 | entry_parms.st_mode |= entry_parms.dperms; |
||
2575 | else if (S_ISLNK (entry_parms.st_mode)) |
||
2576 | entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !? |
||
2577 | else // file or device node |
||
2578 | entry_parms.st_mode |= entry_parms.perms; |
||
1 | pmbaty | 2579 | |
7 | pmbaty | 2580 | add_fsentry (&fsentries, &fsentry_count, &entry_parms, path_in_ifs, path_on_buildhost); // and add filesystem entry |
1 | pmbaty | 2581 | |
11 | pmbaty | 2582 | if (entry_parms.data.bytes != NULL) |
2583 | free (entry_parms.data.bytes); // if blob data was allocated, free it |
||
1 | pmbaty | 2584 | } |
2585 | |||
2586 | // write IFS file |
||
7 | pmbaty | 2587 | fp = fopen (ifs_pathname, "w+b"); |
1 | pmbaty | 2588 | if (fp == NULL) |
15 | pmbaty | 2589 | DIE_WITH_EXITCODE (1, "failed to open \"%s\" for writing: %s", ifs_pathname, strerror (errno)); |
1 | pmbaty | 2590 | |
2 | pmbaty | 2591 | // do we have a startup file ? if so, this is a bootable image |
2592 | if (startupfile_pathname != NULL) |
||
1 | pmbaty | 2593 | { |
2594 | // write boot prefix |
||
11 | pmbaty | 2595 | // ###################################################################################################################################################################################################################################### |
2596 | // # FIXME: figure out how to re-create it |
||
2597 | // ###################################################################################################################################################################################################################################### |
||
2 | pmbaty | 2598 | fwrite_filecontents (bootfile_pathname, fp); |
1 | pmbaty | 2599 | PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary |
2600 | |||
2601 | startupheader_offset = ftell (fp); // save startup header offset |
||
2602 | memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header |
||
2603 | memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb |
||
2604 | startup_header.version = 1; |
||
2605 | startup_header.flags1 = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2) |
||
2606 | startup_header.header_size = sizeof (startup_header); // 256 |
||
2607 | if (strcmp (image_processor, "x86_64") == 0) |
||
10 | pmbaty | 2608 | startup_header.machine = ELF_MACHINE_X86_64; // EM_X86_64 |
1 | pmbaty | 2609 | else if (strcmp (image_processor, "aarch64le") == 0) |
10 | pmbaty | 2610 | startup_header.machine = ELF_MACHINE_AARCH64; // EM_AARCH64 |
1 | pmbaty | 2611 | else |
15 | pmbaty | 2612 | DIE_WITH_EXITCODE (1, "unsupported processor type '%s' found in build file \"%s\"", image_processor, buildfile_pathname); // should not happen |
1 | pmbaty | 2613 | startup_header.startup_vaddr = image_base + (uint32_t) startupfile_ep_from_imagebase; // [I ] Virtual Address to transfer to after IPL is done, here 0x01403008 (appears in "Entry" column for "startup.*") |
2 | pmbaty | 2614 | startup_header.image_paddr = image_base + (uint32_t) bootfile_size; // F[IS] Physical address of image, here 0x01400f30 (appears in "Offset" column for "startup-header" which is the first entry/start of file) |
1 | pmbaty | 2615 | startup_header.ram_paddr = startup_header.image_paddr; // [IS] Physical address of RAM to copy image to (startup_size bytes copied), here 0x01400f30 (same as above) |
2616 | startup_header.ram_size = WILL_BE_FILLED_LATER; // [ S] Amount of RAM used by the startup program and executables contained in the file system, here 0x00cd6128 i.e. 13 459 752 dec. which is 13 Mb. i.e. IFS file size minus 0x9eee (40686) |
||
2617 | startup_header.startup_size = WILL_BE_FILLED_LATER; // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes |
||
2 | pmbaty | 2618 | startup_header.stored_size = WILL_BE_FILLED_LATER; // [I ] Size of entire image, here 0x00cd6128 (same as ram_size) |
1 | pmbaty | 2619 | startup_header.imagefs_size = WILL_BE_FILLED_LATER; // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes |
2 | pmbaty | 2620 | startup_header.preboot_size = (uint16_t) bootfile_size; // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file)) |
8 | pmbaty | 2621 | fwrite_or_die (&startup_header, 1, sizeof (startup_header), fp); // write startup header |
1 | pmbaty | 2622 | PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary |
2623 | |||
2624 | // ###################################################################################################################################################################################################################################### |
||
11 | pmbaty | 2625 | // # FIXME: figure out how to re-create it: |
2626 | // first: open "startup-x86" ELF file, |
||
2627 | // lookup section headers table (there is no program headers table in this one) |
||
2628 | // FIXME: figure out something in there where the result is 0x1401030 !!! |
||
2629 | // then: call the linker: ld --sysroot=${QNX_TARGET}/x86_64/ -T${QNX_TARGET}/x86_64/lib/nto.link --section-start .text=0x1401030 --no-relax ${QNX_TARGET}/x86_64/boot/sys/startup-x86 -o startup.bin.UNSTRIPPED |
||
2630 | // then: parse resulting ELF file, take all program segments and concatenate them --> this is the blob (FIXME: wrong?) |
||
1 | pmbaty | 2631 | // ###################################################################################################################################################################################################################################### |
11 | pmbaty | 2632 | #if 0 // nonworking |
2633 | { |
||
2634 | buffer_t startupfile; |
||
2635 | elf_section_header_t *shdr_text; |
||
2636 | size_t segment_len; |
||
2637 | FILE *control_fp = fopen ("startup.bin.MYSTRIPPED", "wb"); |
||
2638 | |||
2639 | if (read_filecontents ("startup.bin.UNSTRIPPED", MKIFS_PATH, &startupfile) == NULL) |
||
15 | pmbaty | 2640 | DIE_WITH_EXITCODE (1, "couldn't read startup-x86"); |
11 | pmbaty | 2641 | elf = (elf_header_t *) startupfile.bytes; // quick access to ELF header |
2642 | table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers |
||
2643 | for (table_index = 0; table_index < table_count; table_index++) // cycle through program headers |
||
2644 | { |
||
2645 | phdr = (elf_program_header_t *) &startupfile.bytes[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header |
||
2646 | segment_len = ELF_GET_NUMERIC (elf, phdr, size_in_file); |
||
2647 | fwrite_or_die (&startupfile.bytes[ELF_GET_NUMERIC (elf, phdr, file_offset)], 1, segment_len, fp); // dump program segment |
||
2648 | fwrite_or_die (&startupfile.bytes[ELF_GET_NUMERIC (elf, phdr, file_offset)], 1, segment_len, control_fp); // dump program segment |
||
2649 | while (segment_len % 4096 > 0) |
||
2650 | { |
||
2651 | fputc (0, control_fp); |
||
2652 | segment_len++; |
||
2653 | } |
||
2654 | } |
||
2655 | |||
2656 | fclose (control_fp); |
||
2657 | free (startupfile.bytes); |
||
2658 | } |
||
2659 | #else // working |
||
1 | pmbaty | 2660 | fwrite_filecontents (startupfile_pathname, fp); // write startup code from blob file |
11 | pmbaty | 2661 | #endif // working |
1 | pmbaty | 2662 | PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary |
2663 | |||
2664 | startuptrailer_offset = ftell (fp); // save startup trailer offset |
||
8 | pmbaty | 2665 | fwrite_or_die (&startup_trailer, 1, sizeof (startup_trailer), fp); // write startup trailer |
1 | pmbaty | 2666 | PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary |
2667 | } |
||
2668 | |||
8 | pmbaty | 2669 | imageheader_offset = ftell (fp); // save image header offset |
1 | pmbaty | 2670 | memset (&image_header, 0, sizeof (image_header)); // prepare image header |
2671 | memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs" |
||
2672 | image_header.flags = IMAGE_FLAGS_TRAILER_V2 | IMAGE_FLAGS_SORTED | IMAGE_FLAGS_INO_BITS; // endian neutral flags, 0x1c (IMAGE_FLAGS_TRAILER_V2 | IMAGE_FLAGS_SORTED | IMAGE_FLAGS_INO_BITS) |
||
2673 | image_header.image_size = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912) |
||
7 | pmbaty | 2674 | image_header.hdr_dir_size = WILL_BE_FILLED_LATER; // size from header to last dirent (here 0x12b8 or 4792) |
1 | pmbaty | 2675 | image_header.dir_offset = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92) |
2676 | image_header.boot_ino[0] = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0) |
||
2677 | image_header.script_ino = image_bootscript_ino; // inode of file for script (here 3) |
||
2678 | image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0") |
||
8 | pmbaty | 2679 | fwrite_or_die (&image_header, 1, sizeof (image_header), fp); // write image header |
1 | pmbaty | 2680 | PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary |
2681 | |||
6 | pmbaty | 2682 | // write image directory (with the wrong file offsets) |
1 | pmbaty | 2683 | imgdir_offset = ftell (fp); |
7 | pmbaty | 2684 | imgdir_size = 0; // measure image dir size on the fly |
1 | pmbaty | 2685 | for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++) |
7 | pmbaty | 2686 | imgdir_size += fwrite_fsentry (&fsentries[fsentry_index], fp); // NOTE: padding is handled in this function |
1 | pmbaty | 2687 | |
8 | pmbaty | 2688 | fwrite_or_die ("\0\0\0\0", 1, 4, fp); // there seems to be 4 bytes of padding after the image directory |
2689 | imgdir_size += 4; |
||
2690 | |||
1 | pmbaty | 2691 | // is it a bootable image with a kernel file ? |
2 | pmbaty | 2692 | if ((startupfile_pathname != NULL) && (kernelfile_pathname != NULL)) |
1 | pmbaty | 2693 | { |
7 | pmbaty | 2694 | // start by writing the startup script data blob, if we have one |
2695 | for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) |
||
2696 | if (fsentries[fsentry_index].header.ino == image_bootscript_ino) |
||
8 | pmbaty | 2697 | break; // locate the startup script directory entry |
7 | pmbaty | 2698 | if (fsentry_index < fsentry_count) // found it ? |
1 | pmbaty | 2699 | { |
2700 | curr_offset = ftell (fp); |
||
2701 | if (curr_offset + fsentries[fsentry_index].u.file.size >= kernelfile_offset) |
||
15 | pmbaty | 2702 | DIE_WITH_EXITCODE (1, "the compiled startup script is too big (%zd bytes, max is %zd) to fit at current offset %zd", (size_t) fsentries[fsentry_index].u.file.size, kernelfile_offset - curr_offset, curr_offset); |
8 | pmbaty | 2703 | fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure |
2704 | fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob |
||
1 | pmbaty | 2705 | fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written |
2706 | } |
||
7 | pmbaty | 2707 | |
2708 | // now write the filesystem entries that may fit before the kernel |
||
2709 | for (;;) |
||
2710 | { |
||
2711 | curr_offset = ftell (fp); // see where we are |
||
2712 | available_space = kernelfile_offset - curr_offset; // measure the available space |
||
2713 | |||
2714 | // look for the biggest one that can fit |
||
2715 | largest_index = 0; |
||
8 | pmbaty | 2716 | largest_size = 0; |
7 | pmbaty | 2717 | for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) |
2718 | { |
||
2719 | if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written || (fsentries[fsentry_index].u.file.size > available_space)) |
||
2720 | continue; // skip all entries that don't have a separate data block, those who were written already and those that wouldn't fit |
||
2721 | if (fsentries[fsentry_index].u.file.size > largest_size) |
||
2722 | { |
||
2723 | largest_size = fsentries[fsentry_index].u.file.size; |
||
2724 | largest_index = fsentry_index; |
||
2725 | } |
||
2726 | } |
||
8 | pmbaty | 2727 | if (largest_size == 0) |
7 | pmbaty | 2728 | break; // found none ? if so, stop searching |
2729 | |||
11 | pmbaty | 2730 | fsentries[largest_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure |
2731 | |||
10 | pmbaty | 2732 | // is the file we're storing a preprocessed ELF file ? |
11 | pmbaty | 2733 | if (fsentries[largest_index].header.ino & IFS_INO_PROCESSED_ELF) |
10 | pmbaty | 2734 | { |
11 | pmbaty | 2735 | elf = (elf_header_t *) fsentries[largest_index].u.file.UNSAVED_databuf; // quick access to ELF header |
10 | pmbaty | 2736 | table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers |
2737 | for (table_index = 0; table_index < table_count; table_index++) |
||
2738 | { |
||
11 | pmbaty | 2739 | phdr = (elf_program_header_t *) &fsentries[largest_index].u.file.UNSAVED_databuf[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header |
2740 | corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset); |
||
2741 | if (ELF_GET_NUMERIC (elf, phdr, size_in_memory) != 0) // only patch the physical address of segments that have an actual size in memory |
||
14 | pmbaty | 2742 | ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + curr_offset - corrective_offset); // patch the physical address member of the program header table |
10 | pmbaty | 2743 | } |
2744 | } |
||
2745 | |||
8 | pmbaty | 2746 | fwrite_or_die (fsentries[largest_index].u.file.UNSAVED_databuf, 1, fsentries[largest_index].u.file.size, fp); // write file data blob |
7 | pmbaty | 2747 | fsentries[largest_index].UNSAVED_was_data_written = true; // and remember this file's data was written |
2748 | } |
||
15 | pmbaty | 2749 | LOG_INFO ("Current offset: 0x%zx", curr_offset); |
2750 | LOG_INFO ("Kernel file offset: 0x%zx", kernelfile_offset); |
||
1 | pmbaty | 2751 | PAD_OUTFILE_TO (kernelfile_offset); // reach the kernel offset |
2752 | |||
8 | pmbaty | 2753 | // now write the QNX kernel |
2754 | for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) |
||
2755 | if (fsentries[fsentry_index].header.ino == image_kernel_ino) |
||
2756 | break; // locate the kernel directory entry (can't fail) |
||
2757 | curr_offset = ftell (fp); // see where we are |
||
2758 | fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure |
||
15 | pmbaty | 2759 | #ifdef PROCNTO_WIP |
2760 | // is the kernel we're storing a preprocessed ELF kernel ? |
||
2761 | if (fsentries[fsentry_index].header.ino & IFS_INO_PROCESSED_ELF) |
||
2762 | { |
||
2763 | elf = (elf_header_t *) fsentries[fsentry_index].u.file.UNSAVED_databuf; // quick access to ELF header |
||
2764 | table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers |
||
2765 | for (table_index = 0; table_index < table_count; table_index++) |
||
2766 | { |
||
2767 | phdr = (elf_program_header_t *) &fsentries[fsentry_index].u.file.UNSAVED_databuf[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header |
||
2768 | corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset); |
||
2769 | if (ELF_GET_NUMERIC (elf, phdr, size_in_memory) != 0) // only patch the physical address of segments that have an actual size in memory |
||
2770 | ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + curr_offset - corrective_offset); // patch the physical address member of the program header table |
||
2771 | } |
||
2772 | } |
||
2773 | #endif // PROCNTO_WIP |
||
11 | pmbaty | 2774 | fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write kernel file data blob |
1 | pmbaty | 2775 | PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary |
8 | pmbaty | 2776 | fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written |
1 | pmbaty | 2777 | } |
2778 | |||
8 | pmbaty | 2779 | // then write all the other files by increasing inode number: ELF files first |
7 | pmbaty | 2780 | for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) |
1 | pmbaty | 2781 | { |
8 | pmbaty | 2782 | if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written // filter out anything that's not a file, and anything that's been already written |
2783 | || (fsentries[fsentry_index].u.file.size < 4) || (memcmp (fsentries[fsentry_index].u.file.UNSAVED_databuf, ELF_MAGIC_STR, 4) != 0)) // filter out anything that's not an ELF file |
||
1 | pmbaty | 2784 | continue; // skip all entries that don't have a separate data block and those who were written already |
2785 | curr_offset = ftell (fp); |
||
8 | pmbaty | 2786 | fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure |
10 | pmbaty | 2787 | |
2788 | // is the file we're storing a preprocessed ELF file ? |
||
2789 | if (fsentries[fsentry_index].header.ino & IFS_INO_PROCESSED_ELF) |
||
2790 | { |
||
2791 | elf = (elf_header_t *) fsentries[fsentry_index].u.file.UNSAVED_databuf; // quick access to ELF header |
||
2792 | table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers |
||
2793 | for (table_index = 0; table_index < table_count; table_index++) |
||
2794 | { |
||
2795 | phdr = (elf_program_header_t *) &fsentries[fsentry_index].u.file.UNSAVED_databuf[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header |
||
11 | pmbaty | 2796 | corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset); |
2797 | if (ELF_GET_NUMERIC (elf, phdr, size_in_memory) != 0) // only patch the physical address of segments that have an actual size in memory |
||
14 | pmbaty | 2798 | ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + curr_offset - corrective_offset); // patch the physical address member of the program header table |
10 | pmbaty | 2799 | } |
2800 | } |
||
2801 | |||
8 | pmbaty | 2802 | fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob |
1 | pmbaty | 2803 | fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written |
2804 | } |
||
8 | pmbaty | 2805 | for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) // other files (non-ELF, e.g. scripts and data files) last |
2806 | { |
||
2807 | if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written) // filter out anything that's not a file, and anything that's been already written |
||
2808 | continue; // skip all entries that don't have a separate data block and those who were written already |
||
2809 | curr_offset = ftell (fp); |
||
2810 | fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure |
||
2811 | fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob |
||
2812 | fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written |
||
2813 | } |
||
2814 | PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary |
||
1 | pmbaty | 2815 | |
2816 | // finally, write trailer (including empty checksum) |
||
8 | pmbaty | 2817 | imagetrailer_offset = ftell (fp); // save image trailer offset |
2818 | fwrite_or_die (&image_trailer, 1, sizeof (image_trailer), fp); // write image trailer |
||
1 | pmbaty | 2819 | PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary |
2820 | |||
2821 | // if we need to pad it to a specific length, do so |
||
2822 | PAD_OUTFILE_TO (image_totalsize); |
||
2823 | final_size = ftell (fp); |
||
2824 | |||
2825 | // see if we are past the image max size, in which case it's an error |
||
2826 | if (final_size > image_maxsize) |
||
15 | pmbaty | 2827 | DIE_WITH_EXITCODE (1, "image file \"%s\" size %zd exceeds max size (%zd)", ifs_pathname, final_size, (size_t) image_maxsize); |
1 | pmbaty | 2828 | |
2 | pmbaty | 2829 | // do we have a startup file ? if so, this is a bootable image |
2830 | if (startupfile_pathname != NULL) |
||
1 | pmbaty | 2831 | { |
2832 | // rewrite startup header with final values |
||
8 | pmbaty | 2833 | fseek_or_die (fp, startupheader_offset, SEEK_SET); |
2834 | startup_header.startup_size = (uint32_t) (imageheader_offset - startupheader_offset); // size of startup header up to image header |
||
2835 | startup_header.imagefs_size = (uint32_t) (final_size - imageheader_offset); // size of uncompressed imagefs |
||
11 | pmbaty | 2836 | startup_header.ram_size = (uint32_t) (final_size - startupheader_offset); |
2837 | startup_header.stored_size = (uint32_t) (final_size - startupheader_offset); |
||
8 | pmbaty | 2838 | fwrite_or_die (&startup_header, 1, sizeof (startup_header), fp); // write startup header |
2839 | } |
||
1 | pmbaty | 2840 | |
8 | pmbaty | 2841 | // rewrite image header with final values |
2842 | fseek_or_die (fp, imageheader_offset, SEEK_SET); |
||
2843 | image_header.image_size = (uint32_t) (final_size - imageheader_offset); // size of uncompressed imagefs |
||
2844 | image_header.hdr_dir_size = sizeof (image_header) + (uint32_t) imgdir_size; // size from start of image header to last dirent |
||
2845 | fwrite_or_die (&image_header, 1, sizeof (image_header), fp); // write image header |
||
2846 | |||
2847 | // rewrite image directory with final offset values |
||
2848 | fseek_or_die (fp, imgdir_offset, SEEK_SET); |
||
2849 | if (image_header.flags & IMAGE_FLAGS_SORTED) |
||
2850 | qsort (&fsentries[1], fsentry_count - 1, sizeof (fsentry_t), fsentry_compare_pathnames_cb); // sort the filesystem entries by pathname |
||
2851 | for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++) |
||
2852 | fwrite_fsentry (&fsentries[fsentry_index], fp); |
||
2853 | |||
2854 | fclose (fp); // ensure everything is flushed |
||
2855 | |||
2856 | // ALL CHECKSUMS AT THE VERY END |
||
2857 | |||
11 | pmbaty | 2858 | read_filecontents (ifs_pathname, ".", &blob); |
2859 | WELLMANNERED_ASSERT (blob.bytes != NULL, "failed to open IFS file for checksumming: %s", strerror (errno)); |
||
8 | pmbaty | 2860 | |
2861 | // do we have a startup file ? if so, this is a bootable image |
||
2862 | if (startupfile_pathname != NULL) |
||
2863 | { |
||
1 | pmbaty | 2864 | // compute SHA-512 checksum and V1 checksum of startup block |
6 | pmbaty | 2865 | if ( ( (startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) |
2866 | || (!(startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))) |
||
2867 | is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness |
||
2868 | else |
||
2869 | is_foreign_endianness = false; // else this header is for the same endianness as us |
||
1 | pmbaty | 2870 | |
11 | pmbaty | 2871 | SHA512 (&blob.bytes[startupheader_offset], startuptrailer_offset - startupheader_offset, &blob.bytes[startuptrailer_offset]); // compute SHA512 checksum and write it in place in blob data |
2872 | checksum = update_checksum (&blob.bytes[startupheader_offset], startuptrailer_offset + SHA512_DIGEST_LENGTH - startupheader_offset, is_foreign_endianness); // compute old checksum |
||
2873 | memcpy (&blob.bytes[startuptrailer_offset + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place |
||
1 | pmbaty | 2874 | } |
2875 | |||
2876 | // compute SHA-512 checksum and V1 checksum of image block |
||
5 | pmbaty | 2877 | if ( ( (image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) |
2878 | || (!(image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))) |
||
2879 | is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness |
||
2880 | else |
||
2881 | is_foreign_endianness = false; // else this header is for the same endianness as us |
||
1 | pmbaty | 2882 | |
11 | pmbaty | 2883 | SHA512 (&blob.bytes[imageheader_offset], imagetrailer_offset - imageheader_offset, &blob.bytes[imagetrailer_offset]); // compute SHA512 checksum and write it in place in blob data |
2884 | checksum = update_checksum (&blob.bytes[imageheader_offset], imagetrailer_offset + SHA512_DIGEST_LENGTH - imageheader_offset, is_foreign_endianness); // compute old checksum |
||
2885 | memcpy (&blob.bytes[imagetrailer_offset + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place |
||
1 | pmbaty | 2886 | |
8 | pmbaty | 2887 | // now rewrite IFS with the correct checksums |
2888 | fp = fopen (ifs_pathname, "wb"); |
||
2889 | WELLMANNERED_ASSERT (fp, "failed to reopen IFS file for checksumming: %s", strerror (errno)); |
||
11 | pmbaty | 2890 | fwrite_or_die (blob.bytes, 1, blob.len, fp); |
1 | pmbaty | 2891 | fclose (fp); |
11 | pmbaty | 2892 | free (blob.bytes); |
8 | pmbaty | 2893 | |
2894 | // finished, exit with a success code |
||
15 | pmbaty | 2895 | LOG_INFO ("Success"); |
1 | pmbaty | 2896 | exit (0); |
2897 | } |
||
2 | pmbaty | 2898 | |
2899 | |||
10 | pmbaty | 2900 | static int dump_ifs_info (const char *ifs_pathname, bool want_everything) |
2 | pmbaty | 2901 | { |
7 | pmbaty | 2902 | #define hex_printf(buf,size,...) do { \ |
10 | pmbaty | 2903 | if (want_everything || ((size) <= 16 * 1024)) /* only print when it's not too big (up to 16 kb) */\ |
7 | pmbaty | 2904 | hex_fprintf (stdout, (buf), (size), 16, __VA_ARGS__); /* use 16 columns in hex output to stdout */ \ |
2905 | else { \ |
||
2906 | printf (__VA_ARGS__); \ |
||
10 | pmbaty | 2907 | hex_fprintf (stdout, (buf), 1024, 16, " first kilobyte:\n"); \ |
7 | pmbaty | 2908 | } \ |
2909 | } while (0) |
||
2 | pmbaty | 2910 | #define BINARY(x) binary ((x), '-', 'x') |
2911 | |||
2912 | static const char *startupheader_flags1_strings[8] = { |
||
2913 | "VIRTUAL", // bit 0 |
||
2914 | "BIGENDIAN", // bit 1 |
||
2915 | "COMPRESS_BIT1", // bit 2 |
||
2916 | "COMPRESS_BIT2", // bit 3 |
||
2917 | "COMPRESS_BIT3", // bit 4 |
||
2918 | "TRAILER_V2", // bit 5 |
||
2919 | "", // bit 6 |
||
2920 | "", // bit 7 |
||
2921 | }; |
||
2922 | static const char *imageheader_flags_strings[8] = { |
||
2923 | "BIGENDIAN", // bit 0 |
||
2924 | "READONLY", // bit 1 |
||
2925 | "INO_BITS", // bit 2 |
||
2926 | "SORTED", // bit 3 |
||
2927 | "TRAILER_V2", // bit 4 |
||
2928 | "", // bit 5 |
||
2929 | "", // bit 6 |
||
2930 | "", // bit 7 |
||
2931 | }; |
||
2932 | |||
2933 | startup_header_t *startup_header = NULL; |
||
8 | pmbaty | 2934 | size_t startupheader_offset = 0; |
2 | pmbaty | 2935 | startup_trailer_v1_t *startup_trailer_v1 = NULL; |
2936 | startup_trailer_v2_t *startup_trailer_v2 = NULL; |
||
8 | pmbaty | 2937 | size_t startuptrailer_offset = 0; |
2 | pmbaty | 2938 | image_header_t *image_header = NULL; |
2939 | size_t imageheader_offset = 0; |
||
2940 | image_trailer_v1_t *image_trailer_v1 = NULL; |
||
2941 | image_trailer_v2_t *image_trailer_v2 = NULL; |
||
3 | pmbaty | 2942 | size_t imagetrailer_offset = 0; |
2943 | fsentry_t **fsentries = NULL; // mallocated |
||
2944 | size_t fsentry_count = 0; |
||
2 | pmbaty | 2945 | fsentry_t *current_fsentry = NULL; |
2946 | char recorded_sha512[2 * SHA512_DIGEST_LENGTH + 1] = ""; |
||
2947 | char computed_sha512[2 * SHA512_DIGEST_LENGTH + 1] = ""; |
||
2948 | size_t startupfile_blobsize = 0; |
||
3 | pmbaty | 2949 | void *reallocated_ptr; |
5 | pmbaty | 2950 | bool is_foreign_endianness; |
2 | pmbaty | 2951 | size_t bootfile_blobsize = 0; |
2952 | size_t current_offset; |
||
3 | pmbaty | 2953 | size_t fsentry_index; |
2954 | size_t nearest_distance; |
||
2955 | size_t nearest_index; |
||
2 | pmbaty | 2956 | size_t byte_index; |
2957 | uint32_t recorded_checksum; |
||
2958 | uint32_t computed_checksum; |
||
11 | pmbaty | 2959 | buffer_t file; |
2 | pmbaty | 2960 | time_t mtime; |
2961 | |||
2962 | // open and read IFS file |
||
11 | pmbaty | 2963 | if (read_filecontents (ifs_pathname, ".", &file) == NULL) |
15 | pmbaty | 2964 | DIE_WITH_EXITCODE (1, "can't open \"%s\" for reading: %s", ifs_pathname, strerror (errno)); |
2 | pmbaty | 2965 | |
4 | pmbaty | 2966 | printf ("QNX In-kernel Filesystem analysis produced by ifstool version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD); |
11 | pmbaty | 2967 | printf ("IFS file \"%s\" - size 0x%zx (%zd) bytes\n", ifs_pathname, file.len, file.len); |
2 | pmbaty | 2968 | |
2969 | // parse file from start to end |
||
2970 | current_offset = 0; |
||
2971 | for (;;) |
||
2972 | { |
||
2973 | // does a startup header start here ? |
||
11 | pmbaty | 2974 | if ((current_offset + sizeof (startup_header_t) < file.len) && (memcmp (&file.bytes[current_offset], "\xeb\x7e\xff\x00", 4) == 0)) |
2 | pmbaty | 2975 | { |
8 | pmbaty | 2976 | startupheader_offset = current_offset; |
11 | pmbaty | 2977 | startup_header = (startup_header_t *) &file.bytes[startupheader_offset]; |
2 | pmbaty | 2978 | |
2979 | // layout: |
||
2980 | // [STARTUP HEADER] |
||
2981 | // (startup file blob) |
||
2982 | // [STARTUP TRAILER v1 or v2] |
||
2983 | |||
2984 | printf ("\n"); |
||
2985 | printf ("Startup header at offset 0x%zx (%zd):\n", current_offset, current_offset); |
||
2986 | printf (" signature = %02x %02x %02x %02x - good\n", startup_header->signature[0], startup_header->signature[1], startup_header->signature[2], startup_header->signature[3]); |
||
2987 | printf (" version = 0x%04x (%d) - %s\n", startup_header->version, startup_header->version, (startup_header->version == 1 ? "looks good" : "???")); |
||
2988 | printf (" flags1 = 0x%02x (%s)\n", startup_header->flags1, describe_uint8 (startup_header->flags1, startupheader_flags1_strings)); |
||
2989 | printf (" flags2 = 0x%02x (%s) - %s\n", startup_header->flags2, BINARY (startup_header->flags2), (startup_header->flags2 == 0 ? "looks good" : "???")); |
||
2990 | printf (" header_size = 0x%04x (%d) - %s\n", startup_header->header_size, startup_header->header_size, (startup_header->header_size == sizeof (startup_header_t) ? "looks good" : "BAD")); |
||
10 | pmbaty | 2991 | printf (" machine = 0x%04x (%d) - %s\n", startup_header->machine, startup_header->machine, (startup_header->machine == ELF_MACHINE_X86_64 ? "x86_64" : (startup_header->machine == ELF_MACHINE_AARCH64 ? "aarch64" : "unknown"))); |
2 | pmbaty | 2992 | printf (" startup_vaddr = 0x%08x (%d) - virtual address to transfer to after IPL is done\n", startup_header->startup_vaddr, startup_header->startup_vaddr); |
2993 | printf (" paddr_bias = 0x%08x (%d) - value to add to physical addresses to get an indirectable pointer value\n", startup_header->paddr_bias, startup_header->paddr_bias); |
||
2994 | printf (" image_paddr = 0x%08x (%d) - physical address of image\n", startup_header->image_paddr, startup_header->image_paddr); |
||
2995 | printf (" ram_paddr = 0x%08x (%d) - physical address of RAM to copy image to (startup_size bytes copied)\n", startup_header->ram_paddr, startup_header->ram_paddr); |
||
2996 | printf (" ram_size = 0x%08x (%d) - amount of RAM used by the startup program and executables in the fs\n", startup_header->ram_size, startup_header->ram_size); |
||
11 | pmbaty | 2997 | printf (" startup_size = 0x%08x (%d) - size of startup (never compressed) - %s\n", startup_header->startup_size, startup_header->startup_size, (current_offset + sizeof (image_header_t) + startup_header->startup_size + (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)) < file.len ? "looks good" : "BAD (IFS file too short)")); |
2 | pmbaty | 2998 | printf (" stored_size = 0x%08x (%d) - size of entire image - %s\n", startup_header->stored_size, startup_header->stored_size, (startup_header->stored_size == startup_header->ram_size ? "looks good" : "???")); |
2999 | printf (" imagefs_paddr = 0x%08x (%d) - set by IPL when startup runs - %s\n", startup_header->imagefs_paddr, startup_header->imagefs_paddr, (startup_header->imagefs_paddr == 0 ? "looks good" : "??? should be zero")); |
||
3000 | printf (" imagefs_size = 0x%08x (%d) - size of uncompressed imagefs\n", startup_header->imagefs_size, startup_header->imagefs_size); |
||
3001 | printf (" preboot_size = 0x%04x (%d) - size of loaded before header - %s\n", startup_header->preboot_size, startup_header->preboot_size, (startup_header->preboot_size == current_offset ? "looks good" : "???")); |
||
3002 | printf (" zero0 = 0x%04x (%d) - zeros - %s\n", startup_header->zero0, startup_header->zero0, (startup_header->zero0 == 0 ? "looks good" : "??? should be zero")); |
||
3003 | printf (" zero[0] = 0x%08x (%d) - zeros - %s\n", startup_header->zero[0], startup_header->zero[0], (startup_header->zero[0] == 0 ? "looks good" : "??? should be zero")); |
||
3004 | printf (" addr_off = 0x%016llx (%lld) - offset for startup_vaddr and [image|ram|imagefs]_paddr - %s\n", startup_header->addr_off, startup_header->addr_off, (startup_header->addr_off == 0 ? "looks good" : "??? should be zero")); |
||
3005 | hex_printf ((uint8_t *) &startup_header->info[0], sizeof (startup_header->info), " info[48] =\n"); |
||
3006 | |||
3007 | // validate that the file can contain up to the startup trailer |
||
11 | pmbaty | 3008 | if (current_offset + startup_header->startup_size > file.len) |
2 | pmbaty | 3009 | { |
15 | pmbaty | 3010 | LOG_WARNING ("this IFS file is corrupted (startup trailer extends past end of file)"); |
7 | pmbaty | 3011 | goto endofdata; |
2 | pmbaty | 3012 | } |
3013 | |||
5 | pmbaty | 3014 | // check if this endianness is ours |
3015 | if ( ( (startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) |
||
3016 | || (!(startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))) |
||
3017 | is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness |
||
3018 | else |
||
3019 | is_foreign_endianness = false; // else this header is for the same endianness as us |
||
3020 | |||
2 | pmbaty | 3021 | // locate the right startup trailer at the right offset |
3022 | if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2) |
||
3023 | { |
||
8 | pmbaty | 3024 | startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v2_t); |
11 | pmbaty | 3025 | startup_trailer_v2 = (startup_trailer_v2_t *) &file.bytes[startuptrailer_offset]; |
2 | pmbaty | 3026 | startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t); |
3027 | } |
||
3028 | else // old V1 trailer |
||
3029 | { |
||
8 | pmbaty | 3030 | startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v1_t); |
11 | pmbaty | 3031 | startup_trailer_v1 = (startup_trailer_v1_t *) &file.bytes[startuptrailer_offset]; |
2 | pmbaty | 3032 | startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t); |
3033 | } |
||
3034 | |||
3035 | current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob |
||
3036 | printf ("\n"); |
||
3037 | printf ("Startup blob at offset 0x%zx (%zd):\n", current_offset, current_offset); |
||
3038 | printf (" size 0x%zx (%zd) bytes\n", startupfile_blobsize, startupfile_blobsize); |
||
11 | pmbaty | 3039 | printf (" checksum %d\n", update_checksum (&file.bytes[current_offset], startupfile_blobsize, is_foreign_endianness)); |
2 | pmbaty | 3040 | |
3041 | current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer |
||
3042 | printf ("\n"); |
||
3043 | printf ("Startup trailer at offset 0x%zx (%zd) - version %d:\n", current_offset, current_offset, (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? 2 : 1)); |
||
3044 | if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2) |
||
3045 | { |
||
3046 | for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++) |
||
3047 | sprintf (&recorded_sha512[2 * byte_index], "%02x", startup_trailer_v2->sha512[byte_index]); |
||
8 | pmbaty | 3048 | strcpy (computed_sha512, SHA512 (startup_header, startuptrailer_offset - startupheader_offset, NULL)); |
2 | pmbaty | 3049 | recorded_checksum = startup_trailer_v2->cksum; |
8 | pmbaty | 3050 | computed_checksum = update_checksum (startup_header, startuptrailer_offset + SHA512_DIGEST_LENGTH - startupheader_offset, is_foreign_endianness); |
3051 | printf (" sha512([0x%zx-0x%zx[) = %s - %s\n", startupheader_offset, startuptrailer_offset, recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD")); |
||
9 | pmbaty | 3052 | printf (" cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset, startuptrailer_offset + SHA512_DIGEST_LENGTH, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD")); |
2 | pmbaty | 3053 | if (strcasecmp (computed_sha512, recorded_sha512) != 0) |
3054 | printf ("Computed SHA-512: %s\n", computed_sha512); |
||
3055 | if (computed_checksum != recorded_checksum) |
||
9 | pmbaty | 3056 | printf ("Computed cksum: 0x%08x\n", computed_checksum); |
2 | pmbaty | 3057 | } |
3058 | else // old v1 trailer |
||
3059 | { |
||
3060 | recorded_checksum = startup_trailer_v1->cksum; |
||
7 | pmbaty | 3061 | computed_checksum = update_checksum (startup_header, sizeof (startup_header) + startupfile_blobsize, is_foreign_endianness); |
9 | pmbaty | 3062 | printf (" cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset, startuptrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD")); |
2 | pmbaty | 3063 | if (computed_checksum != recorded_checksum) |
9 | pmbaty | 3064 | printf ("Computed cksum: 0x%08x\n", computed_checksum); |
2 | pmbaty | 3065 | } |
3066 | |||
3 | pmbaty | 3067 | current_offset += (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (startup_trailer_v2_t) : sizeof (startup_trailer_v1_t)); // now reach the next segment |
2 | pmbaty | 3068 | } |
3069 | |||
3070 | // else does an image header start here ? |
||
11 | pmbaty | 3071 | else if ((current_offset + sizeof (image_header_t) < file.len) && (memcmp (&file.bytes[current_offset], "imagefs", 7) == 0)) |
2 | pmbaty | 3072 | { |
3073 | imageheader_offset = current_offset; |
||
11 | pmbaty | 3074 | image_header = (image_header_t *) &file.bytes[imageheader_offset]; |
2 | pmbaty | 3075 | |
3076 | // layout: |
||
3077 | // [IMAGE HEADER] |
||
3078 | // [image directory entries] |
||
6 | pmbaty | 3079 | // [smallest file blobs up to KERNEL] |
2 | pmbaty | 3080 | // [padding] |
3081 | // [KERNEL] |
||
6 | pmbaty | 3082 | // [rest of file blobs] |
2 | pmbaty | 3083 | // [IMAGE FOOTER] |
3084 | |||
3085 | printf ("\n"); |
||
3086 | printf ("Image header at offset %zx (%zd):\n", current_offset, current_offset); |
||
3087 | printf (" signature = %02x %02x %02x %02x %02x %02x %02x (\"%.7s\") - good\n", image_header->signature[0], image_header->signature[1], image_header->signature[2], image_header->signature[3], image_header->signature[4], image_header->signature[5], image_header->signature[6], image_header->signature); |
||
3088 | printf (" flags = 0x%02x (%s)\n", image_header->flags, describe_uint8 (image_header->flags, imageheader_flags_strings)); |
||
11 | pmbaty | 3089 | printf (" image_size = 0x%08x (%d) - size from header to end of trailer - %s\n", image_header->image_size, image_header->image_size, (current_offset + image_header->image_size <= file.len ? "looks good" : "BAD (IFS file too short)")); |
3090 | printf (" hdr_dir_size = 0x%08x (%d) - size from header to last dirent - %s\n", image_header->hdr_dir_size, image_header->hdr_dir_size, (current_offset + image_header->hdr_dir_size < file.len ? "looks good" : "BAD (IFS file too short)")); |
||
3091 | printf (" dir_offset = 0x%08x (%d) - offset from header to first dirent - %s\n", image_header->dir_offset, image_header->dir_offset, (current_offset + image_header->dir_offset >= file.len ? "BAD (IFS file too short)" : (image_header->dir_offset > image_header->hdr_dir_size ? "BAD" : "looks good"))); |
||
2 | pmbaty | 3092 | printf (" boot_ino[4] = { 0x%08x, 0x%08x, 0x%08x, 0x%08x }\n", image_header->boot_ino[0], image_header->boot_ino[1], image_header->boot_ino[2], image_header->boot_ino[3]); |
3093 | printf (" script_ino = 0x%08x (%d) - inode of compiled bootscript\n", image_header->script_ino, image_header->script_ino); |
||
3094 | printf (" chain_paddr = 0x%08x (%d) - offset to next fs signature\n", image_header->chain_paddr, image_header->chain_paddr); |
||
3095 | hex_printf ((uint8_t *) &image_header->spare[0], sizeof (image_header->spare), " spare[10] =\n"); |
||
3096 | printf (" mountflags = 0x%08x (%s %s %s %s)\n", image_header->mountflags, BINARY (((uint8_t *) &image_header->mountflags)[0]), BINARY (((uint8_t *) &image_header->mountflags)[1]), BINARY (((uint8_t *) &image_header->mountflags)[2]), BINARY (((uint8_t *) &image_header->mountflags)[3])); |
||
3097 | printf (" mountpoint = \"%s\"\n", image_header->mountpoint); |
||
3098 | |||
3099 | // validate that the file can contain up to the image trailer |
||
11 | pmbaty | 3100 | if (current_offset + image_header->image_size > file.len) |
2 | pmbaty | 3101 | { |
15 | pmbaty | 3102 | LOG_WARNING ("this IFS file is corrupted (image trailer extends past end of file)"); |
7 | pmbaty | 3103 | goto endofdata; |
2 | pmbaty | 3104 | } |
3105 | |||
5 | pmbaty | 3106 | // check if this endianness is ours |
3107 | if ( ( (image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) |
||
3108 | || (!(image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))) |
||
3109 | is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness |
||
3110 | else |
||
3111 | is_foreign_endianness = false; // else this header is for the same endianness as us |
||
3112 | |||
2 | pmbaty | 3113 | // locate the image trailer at the right offset |
3114 | if (image_header->flags & IMAGE_FLAGS_TRAILER_V2) |
||
3 | pmbaty | 3115 | { |
3116 | imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t); |
||
11 | pmbaty | 3117 | image_trailer_v2 = (image_trailer_v2_t *) &file.bytes[imagetrailer_offset]; |
3 | pmbaty | 3118 | } |
2 | pmbaty | 3119 | else // old V1 trailer |
3 | pmbaty | 3120 | { |
3121 | imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t); |
||
11 | pmbaty | 3122 | image_trailer_v1 = (image_trailer_v1_t *) &file.bytes[imagetrailer_offset]; |
3 | pmbaty | 3123 | } |
2 | pmbaty | 3124 | |
3125 | current_offset += sizeof (image_header_t); // jump over the image header and reach the first directory entry |
||
3 | pmbaty | 3126 | |
3127 | // there may be padding before the first directory entry |
||
2 | pmbaty | 3128 | if (image_header->dir_offset - sizeof (image_header_t) > 0) |
11 | pmbaty | 3129 | hex_printf (&file.bytes[current_offset], image_header->dir_offset - sizeof (image_header_t), "\n" "%zd padding bytes at offset 0x%zd (%zd):\n", image_header->dir_offset - sizeof (image_header_t), current_offset, current_offset); |
3 | pmbaty | 3130 | current_offset += image_header->dir_offset - sizeof (image_header_t); // padding was processed, jump over it |
2 | pmbaty | 3131 | |
3132 | // dump all directory entries until the last one included |
||
3 | pmbaty | 3133 | fsentries = NULL; |
3134 | fsentry_count = 0; |
||
6 | pmbaty | 3135 | while (current_offset < imageheader_offset + image_header->hdr_dir_size) |
2 | pmbaty | 3136 | { |
11 | pmbaty | 3137 | current_fsentry = (fsentry_t *) &file.bytes[current_offset]; |
2 | pmbaty | 3138 | |
3139 | if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header)) |
||
3140 | break; // end padding reached |
||
3141 | |||
3 | pmbaty | 3142 | // stack up the filesystem entry pointers in an array while we read them |
3143 | reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *)); |
||
8 | pmbaty | 3144 | WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); |
3 | pmbaty | 3145 | fsentries = reallocated_ptr; |
3146 | fsentries[fsentry_count] = current_fsentry; |
||
3147 | fsentry_count++; |
||
3148 | |||
2 | pmbaty | 3149 | printf ("\n"); |
3150 | printf ("Filesystem entry at offset 0x%zx (%zd) - last one at 0x%zd (%zd):\n", current_offset, current_offset, imageheader_offset + image_header->hdr_dir_size, imageheader_offset + image_header->hdr_dir_size); |
||
11 | pmbaty | 3151 | printf (" size = 0x%04x (%d) - size of dirent - %s\n", current_fsentry->header.size, current_fsentry->header.size, ((current_fsentry->header.size > 0) && (current_offset + current_fsentry->header.size < file.len) ? "looks good" : "BAD")); |
2 | pmbaty | 3152 | printf (" extattr_offset = 0x%04x (%d) - %s\n", current_fsentry->header.extattr_offset, current_fsentry->header.extattr_offset, (current_fsentry->header.extattr_offset == 0 ? "no extattr" : "has extattr")); |
3153 | printf (" ino = 0x%08x (%d) - inode number (%s%s%s%s)\n", current_fsentry->header.ino, current_fsentry->header.ino, (current_fsentry->header.ino & 0xE0000000 ? "is" : "nothing special"), (current_fsentry->header.ino & IFS_INO_PROCESSED_ELF ? " PROCESSED_ELF" : ""), (current_fsentry->header.ino & IFS_INO_RUNONCE_ELF ? " RUNONCE_ELF" : ""), (current_fsentry->header.ino & IFS_INO_BOOTSTRAP_EXE ? " BOOTSTRAP_EXE" : "")); |
||
3154 | printf (" mode = 0x%08x (%d) - %s (0%o), POSIX permissions 0%o\n", current_fsentry->header.mode, current_fsentry->header.mode, (S_ISDIR (current_fsentry->header.mode) ? "directory" : (S_ISREG (current_fsentry->header.mode) ? "file" : (S_ISLNK (current_fsentry->header.mode) ? "symlink" : "device"))), (current_fsentry->header.mode & 0xF000) >> 12, current_fsentry->header.mode & 0xFFF); |
||
3155 | printf (" gid = 0x%08x (%d) - owner group ID%s\n", current_fsentry->header.gid, current_fsentry->header.gid, (current_fsentry->header.gid == 0 ? " (root)" : "")); |
||
3156 | printf (" uid = 0x%08x (%d) - owner user ID%s\n", current_fsentry->header.uid, current_fsentry->header.uid, (current_fsentry->header.uid == 0 ? " (root)" : "")); |
||
3157 | mtime = (time_t) current_fsentry->header.mtime; |
||
3158 | printf (" mtime = 0x%08x (%d) - POSIX timestamp: %s", current_fsentry->header.mtime, current_fsentry->header.mtime, asctime (localtime (&mtime))); // NOTE: asctime() provides the newline |
||
3159 | if (S_ISDIR (current_fsentry->header.mode)) |
||
3160 | printf (" [DIRECTORY] path = \"%s\"\n", (char *) ¤t_fsentry->u.dir.path); // convert from pointer to char array |
||
3161 | else if (S_ISREG (current_fsentry->header.mode)) |
||
3162 | { |
||
11 | pmbaty | 3163 | printf (" [FILE] offset = 0x%08x (%d) - %s\n", current_fsentry->u.file.offset, current_fsentry->u.file.offset, (imageheader_offset + current_fsentry->u.file.offset < file.len ? "looks good" : "BAD (IFS file too short)")); |
3164 | printf (" [FILE] size = 0x%08x (%d) - %s\n", current_fsentry->u.file.size, current_fsentry->u.file.size, (imageheader_offset + current_fsentry->u.file.offset + current_fsentry->u.file.size < file.len ? "looks good" : "BAD (IFS file too short)")); |
||
2 | pmbaty | 3165 | printf (" [FILE] path = \"%s\"\n", (char *) ¤t_fsentry->u.file.path); // convert from pointer to char array |
3166 | } |
||
3167 | else if (S_ISLNK (current_fsentry->header.mode)) |
||
3168 | { |
||
3169 | printf (" [SYMLINK] sym_offset = 0x%04x (%d) - %s\n", current_fsentry->u.symlink.sym_offset, current_fsentry->u.symlink.sym_offset, (sizeof (current_fsentry->header) + 2 * sizeof (uint16_t) + current_fsentry->u.symlink.sym_offset <= current_fsentry->header.size ? "looks good" : "BAD (dirent too short)")); |
||
3 | pmbaty | 3170 | printf (" [SYMLINK] sym_size = 0x%04x (%d) - %s\n", current_fsentry->u.symlink.sym_size, current_fsentry->u.symlink.sym_size, (sizeof (current_fsentry->header) + 2 * sizeof (uint16_t) + current_fsentry->u.symlink.sym_offset + current_fsentry->u.symlink.sym_size <= current_fsentry->header.size ? "looks good" : "BAD (dirent too short)")); |
2 | pmbaty | 3171 | printf (" [SYMLINK] path = \"%s\"\n", (char *) ¤t_fsentry->u.symlink.path); // convert from pointer to char array |
3172 | printf (" [SYMLINK] contents = \"%s\"\n", ((char *) ¤t_fsentry->u.symlink.path) + current_fsentry->u.symlink.sym_offset); // convert from pointer to char array |
||
3173 | } |
||
3174 | else // can only be a device |
||
3175 | { |
||
3176 | printf (" [DEVICE] dev = 0x%08x (%d)\n", current_fsentry->u.device.dev, current_fsentry->u.device.dev); |
||
3177 | printf (" [DEVICE] rdev = 0x%08x (%d)\n", current_fsentry->u.device.rdev, current_fsentry->u.device.rdev); |
||
3178 | printf (" [DEVICE] path = \"%s\"\n", (char *) ¤t_fsentry->u.device.path); // convert from pointer to char array |
||
3179 | } |
||
3180 | |||
11 | pmbaty | 3181 | if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.len)) |
7 | pmbaty | 3182 | { |
15 | pmbaty | 3183 | LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)"); |
7 | pmbaty | 3184 | goto endofdata; |
3185 | } |
||
3186 | |||
2 | pmbaty | 3187 | current_offset += current_fsentry->header.size; |
3188 | } |
||
3 | pmbaty | 3189 | if (imageheader_offset + image_header->hdr_dir_size < current_offset + sizeof (current_fsentry->header)) |
11 | pmbaty | 3190 | hex_printf (&file.bytes[current_offset], imageheader_offset + image_header->hdr_dir_size - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + image_header->hdr_dir_size - current_offset, current_offset, current_offset); |
3 | pmbaty | 3191 | current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // padding was processed, jump over it |
2 | pmbaty | 3192 | |
3 | pmbaty | 3193 | // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data |
3194 | if (fsentry_count > 0) |
||
3195 | { |
||
3196 | while (current_offset < imagetrailer_offset) // and parse data up to the trailer |
||
3197 | { |
||
3198 | nearest_distance = SIZE_MAX; |
||
3199 | nearest_index = SIZE_MAX; |
||
3200 | for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++) |
||
3201 | if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)... |
||
3202 | && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ... |
||
3203 | && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset < nearest_distance)) // ... AND it's the closest to us we've found so far |
||
3204 | { |
||
3205 | nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it |
||
3206 | nearest_index = fsentry_index; |
||
3207 | } |
||
3208 | if (nearest_index == SIZE_MAX) |
||
3209 | break; // found no file ahead, which means we've parsed the whole file data area, so stop the loop so as to proceed to the image trailer |
||
2 | pmbaty | 3210 | |
3 | pmbaty | 3211 | fsentry_index = nearest_index; |
3212 | current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry |
||
2 | pmbaty | 3213 | |
3 | pmbaty | 3214 | // there may be padding before the file data |
3215 | if (imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset > 0) |
||
11 | pmbaty | 3216 | hex_printf (&file.bytes[current_offset], imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, current_offset, current_offset); |
3 | pmbaty | 3217 | current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // padding was processed, jump over it |
3218 | |||
3219 | printf ("\n"); |
||
3220 | printf ("File data blob at offset 0x%zx (%zd):\n", current_offset, current_offset); |
||
3221 | printf (" corresponding dirent index: %zd/%zd\n", fsentry_index, fsentry_count); |
||
6 | pmbaty | 3222 | printf (" corresponding inode 0x%08x (%d) -%s%s%s%s\n", current_fsentry->header.ino, current_fsentry->header.ino, (current_fsentry->header.ino & 0xE0000000 ? "" : " nothing special"), (current_fsentry->header.ino & IFS_INO_PROCESSED_ELF ? " PROCESSED_ELF" : ""), (current_fsentry->header.ino & IFS_INO_RUNONCE_ELF ? " RUNONCE_ELF" : ""), (current_fsentry->header.ino & IFS_INO_BOOTSTRAP_EXE ? " BOOTSTRAP_EXE" : "")); |
3 | pmbaty | 3223 | printf (" corresponding path: \"%s\"\n", (char *) ¤t_fsentry->u.file.path); // convert from pointer to char array |
3224 | printf (" size 0x%zx (%zd) bytes\n", (size_t) current_fsentry->u.file.size, (size_t) current_fsentry->u.file.size); |
||
11 | pmbaty | 3225 | if (current_offset + 4 < file.len) |
3226 | hex_printf (&file.bytes[current_offset], current_fsentry->u.file.size, " data:\n"); |
||
3227 | if (current_offset + current_fsentry->u.file.size < file.len) |
||
3228 | printf (" checksum %d\n", update_checksum (&file.bytes[current_offset], current_fsentry->u.file.size, is_foreign_endianness)); |
||
7 | pmbaty | 3229 | else |
3230 | { |
||
15 | pmbaty | 3231 | LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)"); |
7 | pmbaty | 3232 | goto endofdata; |
3233 | } |
||
3 | pmbaty | 3234 | |
3235 | current_offset += current_fsentry->u.file.size; // now jump over this file's data |
||
3236 | } |
||
3237 | } |
||
3238 | |||
3239 | // ad this point we're past the last file data, there may be padding before the image trailer |
||
3240 | if (imagetrailer_offset - current_offset > 0) |
||
11 | pmbaty | 3241 | hex_printf (&file.bytes[current_offset], imagetrailer_offset - current_offset, "\n" "%zd padding bytes at offset %zx (%zd):\n", imagetrailer_offset - current_offset, current_offset, current_offset); |
3 | pmbaty | 3242 | current_offset += imagetrailer_offset - current_offset; // padding was processed, jump over it |
3243 | |||
3244 | printf ("\n"); |
||
3245 | printf ("Image trailer at offset 0x%zx (%zd) - version %d:\n", current_offset, current_offset, (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? 2 : 1)); |
||
3246 | if (image_header->flags & IMAGE_FLAGS_TRAILER_V2) |
||
3247 | { |
||
3248 | for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++) |
||
3249 | sprintf (&recorded_sha512[2 * byte_index], "%02x", image_trailer_v2->sha512[byte_index]); |
||
8 | pmbaty | 3250 | strcpy (computed_sha512, SHA512 (image_header, imagetrailer_offset - imageheader_offset, NULL)); |
3 | pmbaty | 3251 | recorded_checksum = image_trailer_v2->cksum; |
8 | pmbaty | 3252 | computed_checksum = update_checksum (image_header, imagetrailer_offset + SHA512_DIGEST_LENGTH - imageheader_offset, is_foreign_endianness); |
3253 | printf (" sha512([0x%zx-0x%zx[) = %s - %s\n", imageheader_offset, imagetrailer_offset, recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD")); |
||
9 | pmbaty | 3254 | printf (" cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset, imagetrailer_offset + SHA512_DIGEST_LENGTH, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD")); |
3 | pmbaty | 3255 | if (strcasecmp (computed_sha512, recorded_sha512) != 0) |
3256 | printf ("Computed SHA-512: %s\n", computed_sha512); |
||
3257 | if (computed_checksum != recorded_checksum) |
||
9 | pmbaty | 3258 | printf ("Computed cksum: 0x%08x\n", computed_checksum); |
3 | pmbaty | 3259 | } |
3260 | else // old v1 trailer |
||
3261 | { |
||
3262 | recorded_checksum = image_trailer_v1->cksum; |
||
7 | pmbaty | 3263 | computed_checksum = update_checksum (image_header, image_header->image_size - sizeof (image_trailer_v1_t), is_foreign_endianness); |
9 | pmbaty | 3264 | printf (" cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset, imagetrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD")); |
3 | pmbaty | 3265 | if (computed_checksum != recorded_checksum) |
9 | pmbaty | 3266 | printf ("Computed cksum: 0x%08x\n", computed_checksum); |
3 | pmbaty | 3267 | } |
3268 | |||
3269 | current_offset += (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now reach the next segment (typically end of file) |
||
2 | pmbaty | 3270 | } |
3271 | |||
3272 | // else it has to be a boot blob, of which we don't know the size, except that it has to fit in 0xffff bytes and be immediately followed by a startup header |
||
3273 | else |
||
3274 | { |
||
3275 | // so scan for the first startup header magic and version (which makes us 6 bytes to scan for, i.e. "\xeb\x7e\xff\x00" for the magic and "\x01\x00" (LSB) for the version 1) |
||
11 | pmbaty | 3276 | for (byte_index = current_offset; byte_index < file.len - 6; byte_index++) |
3277 | if (memcmp (&file.bytes[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0) |
||
2 | pmbaty | 3278 | break; // stop as soon as we find it |
3279 | |||
11 | pmbaty | 3280 | if (byte_index >= file.len - 6) |
2 | pmbaty | 3281 | break; // if not found, stop scanning |
3282 | |||
3283 | bootfile_blobsize = byte_index - current_offset; |
||
3284 | printf ("Boot blob at offset 0x%zx (%zd):\n", current_offset, current_offset); |
||
3285 | printf (" size 0x%zx (%zd) bytes\n", bootfile_blobsize, bootfile_blobsize); |
||
11 | pmbaty | 3286 | printf (" checksum 0x%08x\n", update_checksum (&file.bytes[current_offset], bootfile_blobsize, false)); // NOTE: endianness is not known yet -- assume same |
2 | pmbaty | 3287 | |
3288 | current_offset = byte_index; // now reach the next segment |
||
3289 | } |
||
3290 | } |
||
3291 | |||
7 | pmbaty | 3292 | endofdata: |
3 | pmbaty | 3293 | // at this point there's nothing left we're able to parse |
11 | pmbaty | 3294 | if (current_offset < file.len) |
3 | pmbaty | 3295 | { |
3296 | printf ("End of identifiable data reached.\n"); |
||
11 | pmbaty | 3297 | hex_printf (&file.bytes[current_offset], file.len - current_offset, "\n" "%zd extra bytes at offset %zx (%zd):\n", file.len - current_offset, current_offset, current_offset); |
3 | pmbaty | 3298 | } |
2 | pmbaty | 3299 | |
11 | pmbaty | 3300 | printf ("End of file reached at offset 0x%zx (%zd)\n", file.len, file.len); |
3 | pmbaty | 3301 | printf ("IFS dissecation complete.\n"); |
2 | pmbaty | 3302 | return (0); |
3 | pmbaty | 3303 | } |
14 | pmbaty | 3304 | |
3305 | |||
3306 | static int create_intermediate_dirs (const char *file_pathname) |
||
3307 | { |
||
3308 | // creates all intermediate directories from root (or cwd) up to file_path |
||
3309 | |||
3310 | char *temp_pathname; |
||
3311 | char *separator; |
||
3312 | size_t string_index; |
||
3313 | size_t length; |
||
3314 | |||
3315 | temp_pathname = strdup (file_pathname); // have a working copy of file_pathname |
||
3316 | if (temp_pathname == NULL) |
||
3317 | return (-1); // on strdup() failure, return an error value (errno is set) |
||
3318 | length = strlen (temp_pathname); |
||
3319 | for (string_index = length - 1; string_index != SIZE_MAX; string_index--) // i.e. loop until it overflows |
||
3320 | if ((temp_pathname[string_index] == '/') || (temp_pathname[string_index] == '\\')) |
||
3321 | break; // look for the last directory separator and stop as soon as we find it |
||
3322 | if (string_index != SIZE_MAX) |
||
3323 | { |
||
3324 | for (; string_index < length; string_index++) |
||
3325 | temp_pathname[string_index] = 0; // if we found one, break there so as to have just the path and clear the rest of the string |
||
3326 | separator = strtok (&temp_pathname[1], "/\\"); // for each separator in the remaining string past the first one... |
||
3327 | while (separator != NULL) |
||
3328 | { |
||
3329 | (void) mkdir (temp_pathname, 0755); // create directories recursively |
||
3330 | temp_pathname[strlen (temp_pathname)] = '/'; // put the separator back |
||
3331 | separator = strtok (NULL, "/\\"); // and look for the next one |
||
3332 | } |
||
3333 | } |
||
3334 | |||
3335 | free (temp_pathname); // release our working copy of file_pathname |
||
3336 | return (0); |
||
3337 | } |
||
3338 | |||
3339 | |||
3340 | static int dump_ifs_contents (const char *ifs_pathname, const char *outdir) |
||
3341 | { |
||
3342 | static char outfile_pathname[MAXPATHLEN] = ""; |
||
3343 | |||
3344 | startup_header_t *startup_header = NULL; |
||
3345 | size_t startupheader_offset = 0; |
||
3346 | image_header_t *image_header = NULL; |
||
3347 | size_t imageheader_offset = 0; |
||
3348 | size_t imagetrailer_offset = 0; |
||
3349 | fsentry_t **fsentries = NULL; // mallocated |
||
3350 | size_t fsentry_count = 0; |
||
3351 | fsentry_t *current_fsentry = NULL; |
||
3352 | size_t startupfile_blobsize = 0; |
||
3353 | struct utimbuf file_times = { 0, 0 }; |
||
3354 | void *reallocated_ptr; |
||
3355 | size_t bootfile_blobsize = 0; |
||
3356 | size_t current_offset; |
||
3357 | size_t fsentry_index; |
||
3358 | size_t nearest_distance; |
||
3359 | size_t nearest_index; |
||
3360 | size_t byte_index; |
||
3361 | buffer_t file; |
||
3362 | FILE *fp; |
||
3363 | |||
3364 | // open and read IFS file |
||
3365 | if (read_filecontents (ifs_pathname, ".", &file) == NULL) |
||
15 | pmbaty | 3366 | DIE_WITH_EXITCODE (1, "can't open \"%s\" for reading: %s\n", ifs_pathname, strerror (errno)); |
14 | pmbaty | 3367 | |
3368 | // create the output directory |
||
3369 | create_intermediate_dirs (outdir); |
||
3370 | (void) mkdir (outdir, 0755); |
||
3371 | |||
3372 | // parse file from start to end |
||
3373 | current_offset = 0; |
||
3374 | for (;;) |
||
3375 | { |
||
3376 | // does a startup header start here ? |
||
3377 | if ((current_offset + sizeof (startup_header_t) < file.len) && (memcmp (&file.bytes[current_offset], "\xeb\x7e\xff\x00", 4) == 0)) |
||
3378 | { |
||
3379 | startupheader_offset = current_offset; |
||
3380 | startup_header = (startup_header_t *) &file.bytes[startupheader_offset]; |
||
3381 | |||
3382 | // layout: |
||
3383 | // [STARTUP HEADER] |
||
3384 | // (startup file blob) |
||
3385 | // [STARTUP TRAILER v1 or v2] |
||
3386 | |||
3387 | // validate that the file can contain up to the startup trailer |
||
3388 | if (current_offset + startup_header->startup_size > file.len) |
||
3389 | { |
||
15 | pmbaty | 3390 | LOG_WARNING ("this IFS file is corrupted (startup trailer extends past end of file)"); |
14 | pmbaty | 3391 | goto endofdata; |
3392 | } |
||
3393 | |||
3394 | // locate the right startup trailer at the right offset |
||
3395 | if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2) |
||
3396 | startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t); |
||
3397 | else // old V1 trailer |
||
3398 | startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t); |
||
3399 | |||
3400 | current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob |
||
3401 | |||
3402 | // write startup blob |
||
3403 | sprintf (outfile_pathname, "%s/STARTUP.BLOB", outdir); |
||
3404 | fp = fopen (outfile_pathname, "wb"); |
||
3405 | WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno)); |
||
3406 | fwrite (&file.bytes[current_offset], 1, startupfile_blobsize, fp); |
||
3407 | fclose (fp); |
||
3408 | |||
3409 | current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer |
||
3410 | current_offset += (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (startup_trailer_v2_t) : sizeof (startup_trailer_v1_t)); // jump over the startup trailer and reach the next segment |
||
3411 | } |
||
3412 | |||
3413 | // else does an image header start here ? |
||
3414 | else if ((current_offset + sizeof (image_header_t) < file.len) && (memcmp (&file.bytes[current_offset], "imagefs", 7) == 0)) |
||
3415 | { |
||
3416 | imageheader_offset = current_offset; |
||
3417 | image_header = (image_header_t *) &file.bytes[imageheader_offset]; |
||
3418 | |||
3419 | // layout: |
||
3420 | // [IMAGE HEADER] |
||
3421 | // [image directory entries] |
||
3422 | // [smallest file blobs up to KERNEL] |
||
3423 | // [padding] |
||
3424 | // [KERNEL] |
||
3425 | // [rest of file blobs] |
||
3426 | // [IMAGE FOOTER] |
||
3427 | |||
3428 | // validate that the file can contain up to the image trailer |
||
3429 | if (current_offset + image_header->image_size > file.len) |
||
3430 | { |
||
15 | pmbaty | 3431 | LOG_WARNING ("this IFS file is corrupted (image trailer extends past end of file)"); |
14 | pmbaty | 3432 | goto endofdata; |
3433 | } |
||
3434 | |||
3435 | // locate the image trailer at the right offset |
||
3436 | if (image_header->flags & IMAGE_FLAGS_TRAILER_V2) |
||
3437 | imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t); |
||
3438 | else // old V1 trailer |
||
3439 | imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t); |
||
3440 | |||
3441 | current_offset += sizeof (image_header_t); // jump over the image header |
||
3442 | current_offset += image_header->dir_offset - sizeof (image_header_t); // jump over possible padding |
||
3443 | |||
3444 | // dump all directory entries until the last one included |
||
3445 | fsentries = NULL; |
||
3446 | fsentry_count = 0; |
||
3447 | while (current_offset < imageheader_offset + image_header->hdr_dir_size) |
||
3448 | { |
||
3449 | current_fsentry = (fsentry_t *) &file.bytes[current_offset]; |
||
3450 | |||
3451 | if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header)) |
||
3452 | break; // end padding reached |
||
3453 | |||
3454 | // stack up the filesystem entry pointers in an array while we read them |
||
3455 | reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *)); |
||
3456 | WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); |
||
3457 | fsentries = reallocated_ptr; |
||
3458 | fsentries[fsentry_count] = current_fsentry; |
||
3459 | fsentry_count++; |
||
3460 | |||
3461 | if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= file.len)) |
||
3462 | { |
||
15 | pmbaty | 3463 | LOG_WARNING ("this IFS file is corrupted (the size of this directory entry is invalid)"); |
14 | pmbaty | 3464 | goto endofdata; |
3465 | } |
||
3466 | |||
3467 | current_offset += current_fsentry->header.size; |
||
3468 | } |
||
3469 | current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // jump over possible padding |
||
3470 | |||
3471 | // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data |
||
3472 | if (fsentry_count > 0) |
||
3473 | { |
||
3474 | while (current_offset < imagetrailer_offset) // and parse data up to the trailer |
||
3475 | { |
||
3476 | nearest_distance = SIZE_MAX; |
||
3477 | nearest_index = SIZE_MAX; |
||
3478 | for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++) |
||
3479 | if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)... |
||
3480 | && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ... |
||
3481 | && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset < nearest_distance)) // ... AND it's the closest to us we've found so far |
||
3482 | { |
||
3483 | nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it |
||
3484 | nearest_index = fsentry_index; |
||
3485 | } |
||
3486 | if (nearest_index == SIZE_MAX) |
||
3487 | break; // found no file ahead, which means we've parsed the whole file data area, so stop the loop so as to proceed to the image trailer |
||
3488 | |||
3489 | fsentry_index = nearest_index; |
||
3490 | current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry |
||
3491 | |||
3492 | current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // jump over possible padding |
||
3493 | |||
3494 | if (current_offset + current_fsentry->u.file.size >= file.len) |
||
3495 | { |
||
15 | pmbaty | 3496 | LOG_WARNING ("this IFS file is corrupted (the size of this file data extends past the IFS size)"); |
14 | pmbaty | 3497 | goto endofdata; |
3498 | } |
||
3499 | |||
3500 | // write filesystem data entry |
||
3501 | if (S_ISDIR (current_fsentry->header.mode)) |
||
3502 | { |
||
3503 | sprintf (outfile_pathname, "%s/%s", outdir, (char *) ¤t_fsentry->u.dir.path); // convert from pointer to char array |
||
3504 | create_intermediate_dirs (outfile_pathname); |
||
3505 | (void) mkdir (outfile_pathname, current_fsentry->header.mode & 0777); |
||
3506 | } |
||
3507 | else if (S_ISLNK (current_fsentry->header.mode)) |
||
3508 | { |
||
3509 | sprintf (outfile_pathname, "%s/%s", outdir, (char *) ¤t_fsentry->u.symlink.path); // convert from pointer to char array |
||
3510 | create_intermediate_dirs (outfile_pathname); |
||
3511 | #ifdef _WIN32 |
||
3512 | fp = fopen (outfile_pathname, "wb"); // on Windows create symlinks as plain files |
||
3513 | WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno)); |
||
3514 | fwrite ((char *) ¤t_fsentry->u.symlink.path + current_fsentry->u.symlink.sym_offset, 1, current_fsentry->u.symlink.sym_size, fp); // convert from pointer to char array |
||
3515 | fclose (fp); |
||
3516 | #else // !_WIN32, thus POSIX |
||
3517 | symlink (current_fsentry->u.symlink.contents, outfile_pathname); // on UNIX systems, just create the symlink for real |
||
3518 | #endif // _WIN32 |
||
3519 | } |
||
3520 | else if (S_ISREG (current_fsentry->header.mode)) |
||
3521 | { |
||
3522 | sprintf (outfile_pathname, "%s/%s", outdir, (char *) ¤t_fsentry->u.file.path); // convert from pointer to char array |
||
3523 | create_intermediate_dirs (outfile_pathname); |
||
3524 | fp = fopen (outfile_pathname, "wb"); // on Windows create symlinks as plain files |
||
3525 | WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno)); |
||
3526 | fwrite (&file.bytes[current_offset], 1, current_fsentry->u.file.size, fp); |
||
3527 | fclose (fp); |
||
3528 | } |
||
3529 | else // must be a device node. Since we might not be the super-user and/or on Win32, create plain file with "X:Y" as data |
||
3530 | { |
||
3531 | sprintf (outfile_pathname, "%s/%s", outdir, (char *) ¤t_fsentry->u.device.path); // convert from pointer to char array |
||
3532 | create_intermediate_dirs (outfile_pathname); |
||
3533 | fp = fopen (outfile_pathname, "wb"); // on Windows create symlinks as plain files |
||
3534 | WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno)); |
||
3535 | fprintf (fp, "%u:%u", current_fsentry->u.device.dev, current_fsentry->u.device.rdev); |
||
3536 | fclose (fp); |
||
3537 | } |
||
3538 | |||
3539 | // set created file mtime |
||
3540 | file_times.actime = current_fsentry->header.mtime; |
||
3541 | file_times.modtime = current_fsentry->header.mtime; |
||
3542 | utime (outfile_pathname, &file_times); |
||
3543 | |||
3544 | // set created file mode |
||
15 | pmbaty | 3545 | #ifndef _WIN32 |
3546 | (void) chmod (outfile_pathname, current_fsentry->header.mode & 0777); // only on POSIX systems |
||
3547 | #endif // !_WIN32 |
||
14 | pmbaty | 3548 | |
3549 | current_offset += current_fsentry->u.file.size; // now jump over this file's data |
||
3550 | } |
||
3551 | } |
||
3552 | |||
3553 | // ad this point we're past the last file data, there may be padding before the image trailer |
||
3554 | current_offset += imagetrailer_offset - current_offset; // jump over possible padding and reach the image trailer |
||
3555 | current_offset += (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now jump over the image trailer and reach the next segment (typically end of file) |
||
3556 | } |
||
3557 | |||
3558 | // else it has to be a boot blob, of which we don't know the size, except that it has to fit in 0xffff bytes and be immediately followed by a startup header |
||
3559 | else |
||
3560 | { |
||
3561 | // so scan for the first startup header magic and version (which makes us 6 bytes to scan for, i.e. "\xeb\x7e\xff\x00" for the magic and "\x01\x00" (LSB) for the version 1) |
||
3562 | for (byte_index = current_offset; byte_index < file.len - 6; byte_index++) |
||
3563 | if (memcmp (&file.bytes[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0) |
||
3564 | break; // stop as soon as we find it |
||
3565 | |||
3566 | if (byte_index >= file.len - 6) |
||
3567 | break; // if not found, stop scanning |
||
3568 | |||
3569 | bootfile_blobsize = byte_index - current_offset; |
||
3570 | |||
3571 | // write boot blob |
||
3572 | sprintf (outfile_pathname, "%s/BOOT.BLOB", outdir); |
||
3573 | fp = fopen (outfile_pathname, "wb"); |
||
3574 | WELLMANNERED_ASSERT (fp, "failed to open '%s': %s", outfile_pathname, strerror (errno)); |
||
3575 | fwrite (&file.bytes[current_offset], 1, bootfile_blobsize, fp); |
||
3576 | fclose (fp); |
||
3577 | |||
3578 | current_offset = byte_index; // now reach the next segment |
||
3579 | } |
||
3580 | } |
||
3581 | |||
3582 | endofdata: |
||
3583 | return (0); |
||
3584 | } |