Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using a custom segment map #192

Open
ryandesign opened this issue Oct 23, 2022 · 2 comments
Open

Using a custom segment map #192

ryandesign opened this issue Oct 23, 2022 · 2 comments

Comments

@ryandesign
Copy link
Contributor

I see that LaunchAPPLServer uses a custom segment map. Is there any documentation that describes why one might make a custom segment map, what effects this has, and what criteria one would use when deciding what should go in each segment?

I assume that Retro68 loads all of the segments into memory at program start time regardless. Is that assumption correct?

I use third-party libraries in my app and with the default segment map all of their code is placed together with my app's code in the Main CODE segment. I tried using a custom segment map that put each library's code is in its own CODE segment, primarily in order to discover how big each library's code ends up being. This appeared to work fine. Separate CODE resources were created for each library and the app still appears to work fine. Is this a valid reason to create a custom segment map? Are there any problems one should anticipate when using a custom segment map?

@autc04
Copy link
Owner

autc04 commented Oct 25, 2022

The main benefits of using segments are:

  • segments are loaded on-demand the first time a function from the segment is called
  • consequently, we can save memory for cases when we just couldn't avoid linking in some unneeded parts of some libraries (e.g., the standard library has a habity of dragging in a lot of locale support code even if you know you'll never need to output floating point numbers different formats for five different countries).
  • segments can be explicitly unloaded (if you know what you are doing)

Remember there was no virtual memory in 1984, so the modern way of only loading the code that's actually needed wasn't available yet.

The LaunchAPPLServer contains a few strategically placed calls to UnloadSeg. If you switch between different connection types, the unneeded code is unloaded to save a few kilobytes of RAM.

@ryandesign
Copy link
Contributor Author

This is good to know, thanks. I know the Mac was designed to load and unload small code segments, but I had been under the impression that Retro68 didn't support that and always loaded all code into RAM, probably from reading this March 2017 article:

Code segmentation

The original MPW compiler used 16-bit offsets and made uses of #pragma segment to organize the codes into different segments, the names of which need to be configured into the linker phase, to be loaded/unloaded dynamically at run-time as and when the respective code is needed. This results in complicated code and causes the generated application to contain multiple segments under its CODE resources, each of which smaller than 32KB. [...]

Retro68, on the other hand, uses GCC which has 32-bit absolute addresses and does not need to stick faithfully to that part of the 68K Mac architecture. It simply places most of the code into a single segment and uses some custom code to make all those addresses point to the right places once the code is loaded. [...]

The MPW architecture of segmenting via #pragma segment is no longer needed, and will simply be ignored by GCC. [...]

So it sounds like that would be true when using the --mac-single linker flag, but not when using the now-default multisegment mode. It looks like you added the multisegment mode in September 2017, so the article was correct at the time but is now outdated, which I didn't realize until now.

Is there any guidance for how to create a custom segment map? Presumably one should begin by copying the default one—which is not entirely straightforward since the default one is implemented in code rather than a copyable text file—but this seems to be a usable starting point:

SEGMENT libstdc++ locale
    */libstdc++.a:locale.o
    */libstdc++.a:locale_faces.o
    */libstdc++.a:locale_init.o

SEGMENT libstdc++ locale-inst
    */libstdc++.a:locale-inst.o

SEGMENT libstdc++ wlocale-inst
    */libstdc++.a:wlocale-inst.o

SEGMENT libstdc++ cp-demangle
    */libstdc++.a:cp-demangle.o

SEGMENT libstdc++
    */libstdc++.a:*

SEGMENT RetroConsole
    */libRetroConsole.a:*

It's not completely equivalent to the original since the resource IDs will be different, but it's the closest I could get since the segmap text file doesn't seem to offer the opportunity to specify what the resource IDs should be.

Then, how should one add to this? Each segment appears to consist of one or more globs mentioning object files in a static library. I presume if one wanted to make a new segment with only specific objects from another static library foo.a one would use ar t foo.a to see what objects are in it. I assume one cannot achieve finer granularity than an object file such that if one wants to be able to put, say, a function bar into a segment, then one should put that function bar into a separate source file. (Or is there a method like #pragma segment that works?)

To reference object files of one's own application which are not part of a static library, such as x.o or y.cpp.obj, it appears that one just lists the object file name (with the */ prefix presumably to mean "in any directory") like */x.o or */y.cpp.obj. Or, I see that LaunchAPPLServer.segmap doesn't specify the extension and uses a glob to allow any extension, as in */AppLauncher.*.

Segment 1 is always created as:

                          "*/libretrocrt.a:start.c.obj",
                          "*/libretrocrt.a:relocate.c.obj",
                          "*/libretrocrt.a:MultiSegApp.c.obj",
                          "*/libretrocrt.a:LoadSeg.s.obj",
                          "*/libretrocrt.a:*",

which seems to say "include these four objects from libretrocrt.a and also include all of the other objects from libretrocrt.a." What's the purpose of listing those four objects separately? Does it enforce that those four objects are placed first in the code segment and in that order? If so, I can understand why start would need to be first, but what about the other three?

Is this all documented somewhere, or could it be? Lack of official documentation is what compels people to seek out third-party articles which can be inaccurate or outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants