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
|
// $2001 Create
|
||||||
// $2002 Destroy
|
// $2002 Destroy
|
||||||
// $2004 ChangePath
|
// $2004 ChangePath
|
||||||
|
// $2008 Volume
|
||||||
// $2010 Open
|
// $2010 Open
|
||||||
// $2012 Read
|
// $2012 Read
|
||||||
// $2013 Write
|
// $2013 Write
|
||||||
|
|
@ -29,6 +30,8 @@
|
||||||
// $2017 GetMark
|
// $2017 GetMark
|
||||||
// $2018 SetEOF
|
// $2018 SetEOF
|
||||||
// $2019 GetEOF
|
// $2019 GetEOF
|
||||||
|
// $2020 GetDevNumber
|
||||||
|
// $202C DInfo
|
||||||
// $2029 Quit (special — no return)
|
// $2029 Quit (special — no return)
|
||||||
// See "GS/OS Reference" for the full ~50 calls and parm-block layouts.
|
// See "GS/OS Reference" for the full ~50 calls and parm-block layouts.
|
||||||
|
|
||||||
|
|
@ -165,6 +168,61 @@ typedef struct {
|
||||||
unsigned long resourceBlocks;// [out]
|
unsigned long resourceBlocks;// [out]
|
||||||
} DirEntryRecGS;
|
} 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
|
// 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
|
// 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
|
// 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 gsosGetPrefix (PrefixRecGS *p);
|
||||||
extern unsigned short gsosGetFileInfo(FileInfoRecGS *p);
|
extern unsigned short gsosGetFileInfo(FileInfoRecGS *p);
|
||||||
extern unsigned short gsosGetDirEntry(DirEntryRecGS *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
|
// 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
|
// iigsGsos.o for bare-metal or libcGno.o for GNO/ME), 0 when only
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@
|
||||||
.globl gsosGetFileInfo
|
.globl gsosGetFileInfo
|
||||||
.globl gsosGetDirEntry
|
.globl gsosGetDirEntry
|
||||||
.globl gsosCreate
|
.globl gsosCreate
|
||||||
|
.globl gsosVolume
|
||||||
|
.globl gsosGetDevNumber
|
||||||
|
.globl gsosDInfo
|
||||||
|
|
||||||
; __gsosIsRealImpl — sentinel that distinguishes a REAL GS/OS dispatch
|
; __gsosIsRealImpl — sentinel that distinguishes a REAL GS/OS dispatch
|
||||||
; surface from the universal-success stub in iigsGsosStub.s. Both
|
; surface from the universal-success stub in iigsGsosStub.s. Both
|
||||||
|
|
@ -104,6 +107,15 @@ __gsosIsRealImpl:
|
||||||
gsosCreate:
|
gsosCreate:
|
||||||
gsosDispatch 0x2001, __gsosCreatePb
|
gsosDispatch 0x2001, __gsosCreatePb
|
||||||
|
|
||||||
|
gsosVolume:
|
||||||
|
gsosDispatch 0x2008, __gsosVolumePb
|
||||||
|
|
||||||
|
gsosGetDevNumber:
|
||||||
|
gsosDispatch 0x2020, __gsosGetDevNumberPb
|
||||||
|
|
||||||
|
gsosDInfo:
|
||||||
|
gsosDispatch 0x202c, __gsosDInfoPb
|
||||||
|
|
||||||
gsosOpen:
|
gsosOpen:
|
||||||
gsosDispatch 0x2010, __gsosOpenPb
|
gsosDispatch 0x2010, __gsosOpenPb
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1017,14 +1017,95 @@ struct Linker {
|
||||||
// helper of the same name don't collide on insertion.
|
// helper of the same name don't collide on insertion.
|
||||||
std::map<std::string, uint32_t> localSyms;
|
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) {
|
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>();
|
auto o = std::make_unique<InputObject>();
|
||||||
o->path = path;
|
o->path = path;
|
||||||
o->raw = readFile(path);
|
o->raw = std::move(data);
|
||||||
o->parse();
|
o->parse();
|
||||||
objs.push_back(std::move(o));
|
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
|
// Resolve a reloc to (target, name) using the symbol table and the
|
||||||
// per-object section base map. Requires link() to have populated
|
// per-object section base map. Requires link() to have populated
|
||||||
// objOff/globalSyms/lastLayout first. Returns false when the
|
// objOff/globalSyms/lastLayout first. Returns false when the
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue