link816 now accepts library archives.
This commit is contained in:
parent
5e0547e72c
commit
4a13f9df26
3 changed files with 156 additions and 1 deletions
|
|
@ -21,6 +21,7 @@
|
|||
// $2001 Create
|
||||
// $2002 Destroy
|
||||
// $2004 ChangePath
|
||||
// $2008 Volume
|
||||
// $2010 Open
|
||||
// $2012 Read
|
||||
// $2013 Write
|
||||
|
|
@ -29,6 +30,8 @@
|
|||
// $2017 GetMark
|
||||
// $2018 SetEOF
|
||||
// $2019 GetEOF
|
||||
// $2020 GetDevNumber
|
||||
// $202C DInfo
|
||||
// $2029 Quit (special — no return)
|
||||
// See "GS/OS Reference" for the full ~50 calls and parm-block layouts.
|
||||
|
||||
|
|
@ -165,6 +168,61 @@ typedef struct {
|
|||
unsigned long resourceBlocks;// [out]
|
||||
} DirEntryRecGS;
|
||||
|
||||
// Class-1 Create parm block ($2001). Set pCount=5 to create a file or a
|
||||
// directory: pathname + access + fileType + auxType + storageType.
|
||||
// storageType 13 ($0D) = directory (use fileType $0F); 1 = seedling file.
|
||||
typedef struct {
|
||||
unsigned short pCount; // 5 (or up to 7 with eof / resourceEOF)
|
||||
void *pathname; // [in] GSString *
|
||||
unsigned short access; // [in]
|
||||
unsigned short fileType; // [in]
|
||||
unsigned long auxType; // [in]
|
||||
unsigned short storageType; // [in] 13 = directory
|
||||
unsigned long eof; // [in] (pCount >= 6)
|
||||
unsigned long resourceEOF; // [in] (pCount >= 7)
|
||||
} CreateRecGS;
|
||||
|
||||
// Class-1 Volume parm block ($2008). Given a device or volume name in
|
||||
// devName, returns block counts + block size; free bytes = freeBlocks *
|
||||
// blockSize. volName must point to a caller-allocated ResultBuf (the OS
|
||||
// writes the volume name there), so pCount is at least 2 whenever freeBlocks
|
||||
// (pCount>=4) is wanted.
|
||||
typedef struct {
|
||||
unsigned short pCount; // 6 for free-space (through blockSize)
|
||||
void *devName; // [in] GSString *
|
||||
void *volName; // [out] ResultBuf *
|
||||
unsigned long totalBlocks; // [out]
|
||||
unsigned long freeBlocks; // [out]
|
||||
unsigned short fileSysID; // [out]
|
||||
unsigned short blockSize; // [out]
|
||||
} VolumeRecGS;
|
||||
|
||||
// Class-1 GetDevNumber parm block ($2020). Maps a device OR volume name to a
|
||||
// device number (used to bridge a volume name to a DInfo call).
|
||||
typedef struct {
|
||||
unsigned short pCount; // 2
|
||||
void *devName; // [in] GSString * (device or volume name)
|
||||
unsigned short devNum; // [out]
|
||||
} DevNumRecGS;
|
||||
|
||||
// Class-1 DInfo parm block ($202C). Given a device number, returns the device
|
||||
// NAME (which is what the Volume call wants) among other characteristics.
|
||||
// pCount=2 fills only devName.
|
||||
typedef struct {
|
||||
unsigned short pCount; // 2 (devNum in, devName out) .. up to 12
|
||||
unsigned short devNum; // [in]
|
||||
void *devName; // [out] ResultBuf *
|
||||
unsigned short characteristics; // [out]
|
||||
unsigned long totalBlocks; // [out]
|
||||
unsigned short slotNum; // [out]
|
||||
unsigned short unitNum; // [out]
|
||||
unsigned short version; // [out]
|
||||
unsigned short deviceID; // [out]
|
||||
unsigned short headLink; // [out]
|
||||
unsigned short forwardLink; // [out]
|
||||
void *extendedDIBPtr; // [out]
|
||||
} DInfoRecGS;
|
||||
|
||||
// Open / Read / Write / Close wrappers. Each returns 0 on success or
|
||||
// a non-zero GS/OS error code (see gsos.h reference for codes). The
|
||||
// parm block lives on the caller's stack; you set the input fields
|
||||
|
|
@ -186,6 +244,10 @@ extern unsigned short gsosChangePath(ChangePathRecGS *p);
|
|||
extern unsigned short gsosGetPrefix (PrefixRecGS *p);
|
||||
extern unsigned short gsosGetFileInfo(FileInfoRecGS *p);
|
||||
extern unsigned short gsosGetDirEntry(DirEntryRecGS *p);
|
||||
extern unsigned short gsosCreate (CreateRecGS *p);
|
||||
extern unsigned short gsosVolume (VolumeRecGS *p);
|
||||
extern unsigned short gsosGetDevNumber(DevNumRecGS *p);
|
||||
extern unsigned short gsosDInfo (DInfoRecGS *p);
|
||||
|
||||
// Returns 1 when a real GS/OS dispatch surface is linked (either
|
||||
// iigsGsos.o for bare-metal or libcGno.o for GNO/ME), 0 when only
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@
|
|||
.globl gsosGetFileInfo
|
||||
.globl gsosGetDirEntry
|
||||
.globl gsosCreate
|
||||
.globl gsosVolume
|
||||
.globl gsosGetDevNumber
|
||||
.globl gsosDInfo
|
||||
|
||||
; __gsosIsRealImpl — sentinel that distinguishes a REAL GS/OS dispatch
|
||||
; surface from the universal-success stub in iigsGsosStub.s. Both
|
||||
|
|
@ -104,6 +107,15 @@ __gsosIsRealImpl:
|
|||
gsosCreate:
|
||||
gsosDispatch 0x2001, __gsosCreatePb
|
||||
|
||||
gsosVolume:
|
||||
gsosDispatch 0x2008, __gsosVolumePb
|
||||
|
||||
gsosGetDevNumber:
|
||||
gsosDispatch 0x2020, __gsosGetDevNumberPb
|
||||
|
||||
gsosDInfo:
|
||||
gsosDispatch 0x202c, __gsosDInfoPb
|
||||
|
||||
gsosOpen:
|
||||
gsosDispatch 0x2010, __gsosOpenPb
|
||||
|
||||
|
|
|
|||
|
|
@ -1017,14 +1017,95 @@ struct Linker {
|
|||
// helper of the same name don't collide on insertion.
|
||||
std::map<std::string, uint32_t> localSyms;
|
||||
|
||||
// Load one ELF object or, when `path` is a Unix `ar` archive, every object
|
||||
// member it holds. Archive support lets JoeyLib examples link against a
|
||||
// real libjoey.a exactly like the other ports; --gc-sections then drops
|
||||
// whatever the image doesn't reference, matching classic member selection.
|
||||
void addObject(const std::string &path) {
|
||||
std::vector<uint8_t> data = readFile(path);
|
||||
static const unsigned char AR_MAGIC[8] =
|
||||
{'!', '<', 'a', 'r', 'c', 'h', '>', '\n'};
|
||||
if (data.size() >= 8 && std::memcmp(data.data(), AR_MAGIC, 8) == 0) {
|
||||
addArchive(path, data);
|
||||
return;
|
||||
}
|
||||
auto o = std::make_unique<InputObject>();
|
||||
o->path = path;
|
||||
o->raw = readFile(path);
|
||||
o->raw = std::move(data);
|
||||
o->parse();
|
||||
objs.push_back(std::move(o));
|
||||
}
|
||||
|
||||
// Split a GNU-format `ar` archive into its object members and load each as
|
||||
// its own InputObject (path "archive.a(member)", so the file-static
|
||||
// local-symbol key name@basename stays unique per member). Skips the
|
||||
// symbol index ("/") and long-name string table ("//").
|
||||
void addArchive(const std::string &path,
|
||||
const std::vector<uint8_t> &data) {
|
||||
size_t pos = 8; // past "!<arch>\n"
|
||||
std::string longNames; // "//" GNU long-name table
|
||||
size_t members = 0;
|
||||
|
||||
while (pos + 60 <= data.size()) {
|
||||
const char *hdr = reinterpret_cast<const char *>(&data[pos]);
|
||||
if (hdr[58] != '`' || hdr[59] != '\n')
|
||||
die("'" + path + "': corrupt archive member header");
|
||||
|
||||
char sizeField[11];
|
||||
std::memcpy(sizeField, hdr + 48, 10);
|
||||
sizeField[10] = '\0';
|
||||
long msize = std::strtol(sizeField, nullptr, 10);
|
||||
if (msize < 0)
|
||||
die("'" + path + "': bad archive member size");
|
||||
size_t dataOff = pos + 60;
|
||||
if (dataOff + static_cast<size_t>(msize) > data.size())
|
||||
die("'" + path + "': archive member overruns file");
|
||||
|
||||
char nameField[17];
|
||||
std::memcpy(nameField, hdr, 16);
|
||||
nameField[16] = '\0';
|
||||
std::string name(nameField);
|
||||
while (!name.empty() && name.back() == ' ') name.pop_back();
|
||||
|
||||
bool isObject = true;
|
||||
if (name == "/" || name == "/SYM64/" ||
|
||||
name == "__.SYMDEF" || name == "__.SYMDEF SORTED") {
|
||||
isObject = false; // archive symbol index
|
||||
} else if (name == "//") {
|
||||
longNames.assign(reinterpret_cast<const char *>(&data[dataOff]),
|
||||
static_cast<size_t>(msize));
|
||||
isObject = false; // GNU long-name string table
|
||||
} else if (name.size() >= 2 && name[0] == '/' &&
|
||||
std::isdigit((unsigned char)name[1])) {
|
||||
size_t off = static_cast<size_t>(
|
||||
std::strtoul(name.c_str() + 1, nullptr, 10));
|
||||
name.clear(); // "/<offset>" -> resolve in "//"
|
||||
for (size_t i = off; i < longNames.size() &&
|
||||
longNames[i] != '\n' && longNames[i] != '/';
|
||||
++i)
|
||||
name.push_back(longNames[i]);
|
||||
} else if (!name.empty() && name.back() == '/') {
|
||||
name.pop_back(); // GNU short name "foo.o/"
|
||||
}
|
||||
|
||||
if (isObject) {
|
||||
auto o = std::make_unique<InputObject>();
|
||||
o->path = path + "(" + name + ")";
|
||||
o->raw.assign(data.begin() + dataOff,
|
||||
data.begin() + dataOff +
|
||||
static_cast<size_t>(msize));
|
||||
o->parse();
|
||||
objs.push_back(std::move(o));
|
||||
++members;
|
||||
}
|
||||
|
||||
pos = dataOff + static_cast<size_t>(msize);
|
||||
if (pos & 1) ++pos; // members are 2-byte aligned
|
||||
}
|
||||
if (members == 0)
|
||||
die("'" + path + "': archive holds no object members");
|
||||
}
|
||||
|
||||
// Resolve a reloc to (target, name) using the symbol table and the
|
||||
// per-object section base map. Requires link() to have populated
|
||||
// objOff/globalSyms/lastLayout first. Returns false when the
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue