forked from rui314/mold
-
Notifications
You must be signed in to change notification settings - Fork 0
/
archive-file.h
153 lines (124 loc) · 3.53 KB
/
archive-file.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#pragma once
#include "mold.h"
#include "filetype.h"
namespace mold {
struct ArHdr {
char ar_name[16];
char ar_date[12];
char ar_uid[6];
char ar_gid[6];
char ar_mode[8];
char ar_size[10];
char ar_fmag[2];
bool starts_with(std::string_view s) const {
return std::string_view(ar_name, s.size()) == s;
}
bool is_strtab() const {
return starts_with("// ");
}
bool is_symtab() const {
return starts_with("/ ") || starts_with("/SYM64/ ");
}
std::string read_name(std::string_view strtab, u8 *&ptr) const {
// BSD-style long filename
if (starts_with("#1/")) {
int namelen = atoi(ar_name + 3);
std::string name{(char *)ptr, (size_t)namelen};
ptr += namelen;
if (size_t pos = name.find('\0'))
name = name.substr(0, pos);
return name;
}
// SysV-style long filename
if (starts_with("/")) {
const char *start = strtab.data() + atoi(ar_name + 1);
return {start, (const char *)strstr(start, "/\n")};
}
// Short fileanme
if (const char *end = (char *)memchr(ar_name, '/', sizeof(ar_name)))
return {ar_name, end};
return {ar_name, sizeof(ar_name)};
}
};
template <typename C>
std::vector<MappedFile<C> *>
read_thin_archive_members(C &ctx, MappedFile<C> *mf) {
u8 *begin = mf->data;
u8 *data = begin + 8;
std::vector<MappedFile<C> *> vec;
std::string_view strtab;
while (data < begin + mf->size) {
// Each header is aligned to a 2 byte boundary.
if ((begin - data) % 2)
data++;
ArHdr &hdr = *(ArHdr *)data;
u8 *body = data + sizeof(hdr);
u64 size = atol(hdr.ar_size);
// Read a string table.
if (hdr.is_strtab()) {
strtab = {(char *)body, (size_t)size};
data = body + size;
continue;
}
// Skip a symbol table.
if (hdr.is_symtab()) {
data = body + size;
continue;
}
if (!hdr.starts_with("#1/") && !hdr.starts_with("/"))
Fatal(ctx) << mf->name << ": filename is not stored as a long filename";
std::string name = hdr.read_name(strtab, body);
// Skip if symbol table
if (name == "__.SYMDEF" || name == "__.SYMDEF SORTED")
continue;
std::string path = name.starts_with('/') ?
name : (filepath(mf->name).parent_path() / name).string();
vec.push_back(MappedFile<C>::must_open(ctx, path));
data = body;
}
return vec;
}
template <typename C>
std::vector<MappedFile<C> *>
read_fat_archive_members(C &ctx, MappedFile<C> *mf) {
u8 *begin = mf->data;
u8 *data = begin + 8;
std::vector<MappedFile<C> *> vec;
std::string_view strtab;
while (begin + mf->size - data >= 2) {
if ((begin - data) % 2)
data++;
ArHdr &hdr = *(ArHdr *)data;
u8 *body = data + sizeof(hdr);
u64 size = atol(hdr.ar_size);
data = body + size;
// Read if string table
if (hdr.is_strtab()) {
strtab = {(char *)body, (size_t)size};
continue;
}
// Skip if symbol table
if (hdr.is_symtab())
continue;
// Read the name field
std::string name = hdr.read_name(strtab, body);
// Skip if symbol table
if (name == "__.SYMDEF" || name == "__.SYMDEF SORTED")
continue;
vec.push_back(mf->slice(ctx, name, body - begin, data - body));
}
return vec;
}
template <typename C>
std::vector<MappedFile<C> *>
read_archive_members(C &ctx, MappedFile<C> *mf) {
switch (get_file_type(mf)) {
case FileType::AR:
return read_fat_archive_members(ctx, mf);
case FileType::THIN_AR:
return read_thin_archive_members(ctx, mf);
default:
unreachable();
}
}
} // namespace mold