blob: 424d3d68da7ad48855bc5b070615f85fba797536 [file] [view] [edit]
# Adding Target Support
LLDB supports many combinations of architecture, operating system and other
system components. In this document we describe the considerations and
requirements for porting LLDB to a combination of those things which we will
refer to as a "target" within this document.
:::{note}
The terms `Target` and `Platform` are used throughout LLDB, often
referring to a subset of what this document calls a "target".
Unfortunately there is no more specific word to use here.
:::
This document provides some hints on implementation, but because every target
is unique, we expect developers to learn about implementation from existing
targets.
This document starts with the end of the process, proposing your target for
upstream inclusion. It is worth reading through this even if that is not one of
your goals, as you will have to tackle the same topics even in a downstream
implementation.
## Upstreaming Proposal Process
You must send an RFC to the
[LLDB Discourse forum](https://discourse.llvm.org/c/subprojects/lldb/8) before
upstreaming new target support. This RFC must be accepted in some form by the
community before any changes can be merged upstream that are specific to your
target.
This RFC follows the normal rules of the LLVM community decision making process.
We require an RFC for target support upstreaming because:
* We want to ensure there is some level of public discussion on the topic.
It is important that details, even those obvious to the community at the time,
are written down so that future readers may learn from them.
* These discussions form the basis for future proposals and the assessment
of those proposals. Every proposal will be different in some way, and by
contrasting current proposals with the previous proposals we can do a better
job assessing them.
RFCs are not required to be:
* In a set format. Make a logical argument in whatever way you think fit.
* Exhaustively detailed. Include what you think is relevant and the community
will ask for the rest.
* Answering all the same questions, or making all the same points, as previous
proposals. Compare your target with existing targets, but not everything will,
or has to, apply to yours.
* A commitment to the maximum theoretical level of support (more on this later).
If your resources are limited, say so, and that will be taken into account
(and vice versa, committing to a lot of work does not guarantee acceptance).
## Removal Proposal Process
Removing target support also requires an RFC. Reasonable effort should be made
to involve the original contributors and users of the target support.
:::{note}
You may propose the removal of a subset of the target. For instance, removing
support for an operating system on architecture A, without removing support for
that operating system on architecture B.
:::
The content of the RFC should cover the same topics as an upstreaming proposal,
but you will be arguing for the opposite conclusion.
We reserve the right to change our standards over time, so a removal proposal is
not necessarily a refutation of each point made in the upstreaming proposal.
For example, what was an acceptable level of testing in the past may no longer
be acceptable.
If standards change and are to be applied to existing targets, target
stakeholders should be given the opportunity to meet those standards rather
than going straight to removal.
## Expectations Of Upstream Code
Listed below are some examples of factors considered when considering accepting
code upstream. These are examples and RFC authors are free to add their own,
leave some out, or explain why they do not apply to their proposal.
If you do use these points, they need to come with an answer and evidence to
justify the answer, rather than simply "yes this applies to my target".
In other words, your proposal must stand alone without requiring readers to read
this document as well.
The first set covers your motivation for your target being upstream, rather
than on a fork:
* Will it help you distribute an LLDB that includes this target support?
For example if there is an existing community for this
target, and how would they acquire LLDB?
* Will it enable a wider community than your own forks would?
For example for it to be included in Linux distribution packaging.
* Will it improve support for other targets by being there?
For example if we already support Operating System X on Architecture Y,
adding Architecture Z support may improve both as a side effect.
* Will it help you keep your changes in sync?
For example if it involves fundamental changes to LLDB, or you have a very
small amount of maintenance resources in your community.
* What other costs (or benefits) do you incur staying on a fork?
For example, your company might already have a fork.
The next set is about whether you, your community, or the LLDB community, can
adequately maintain the code upstream:
* Who will be the maintainers for this target? Ideally there will be more than
one, who is present in the LLVM community and can be contacted
in a few different ways.
* How often will it be tested, where, by whom and who will be responsible for
it?
* Who will address problems with it? Will it always be the named maintainers?
Is it so common that anyone in upstream LLDB can deal with it, or will
only employees of a specific company work on it.
* When it breaks, how easy will it be for the upstream LLDB community to
continue their work without disruption?
* If upstream contributors want to reproduce issues on your target, how can
they access it? Can it be emulated or virtualized? Does it require them to
sign a license? Do you offer access for open source projects? (and does that
include employees of other companies)
## The Extent of Target Support
The factors above are part of a cost benefit analysis that the LLDB community
will do, prioritising the health of the community and the project. This means
that there is no set level or type of commitment required for a new target.
Each case will have unique aspects, so you need to tell the community how much
impact your target will have on LLDB. Below are some questions you can start
with, along with stereotypical "big" and "small" answers.
Note that these are intentionally not maximums and minimums. The impact
of some targets will be bigger than "big" or smaller than "small".
* How many users will use LLDB with this target?
* Big: millions of developers worldwide.
* Small: you and a small community of target users.
* How many changes will it require?
* Big: large changes to all parts of LLDB. On the level of the existing
code for C and C++ support.
* Small: small changes to enable existing support from LLVM, changes
to packet parsing (see the MSP430 case study below).
* What parts and features of LLDB will work on, or with, this target?
* Big: `lldb`, `lldb-server` and advanced features like shared libraries.
* Small: just `lldb` and only basic features like continue, stop, reading
memory and registers.
* How often is it tested?
* Big: per-commit testing of LLDB, following the upstream llvm-project.
* Small: per release of a downstream community or individual developer's
tools.
* Who will maintain it?
* Big: there are several listed maintainers for this target, who are
employed by a company with significant investments in the target.
* Small: there is a single maintainer listed for this target.
* When it breaks, who will be affected?
* Big: every single developer and user of LLDB.
* Small: you and your target's community.
* How do I reproduce a problem on this target?
* Big: all components are open source and can be run anywhere by using
simulations with very little setup.
* Small: you contact the maintainer and they do it for you.
* Who will fix problems?
* Big: the maintainers and a large group of contributors.
* Small: only the listed maintainer.
## Target Support Case Studies
These case studies are to give you starting points for your proposals. Consider
how your target compares to these.
### Apple Targets
Apple targets have possibly the most extensive support in LLDB.
* Changes from LLVM's main branch are continuously tested. Results are
accessible publicly, and reported to all LLDB contributors.
* Many employees contribute upstream and are maintainers for these targets.
* Target-specific problems are either solved by the upstream community with
maintainer input, or by the maintainers themselves.
* Apple-specific features either do not impact other targets, or, when they
do, they are designed and maintained in collaboration with the community.
* Many people unrelated to Apple itself use Apple hardware, which is
available at retail. So it is fairly easy to find someone who can reproduce
an issue.
* LLDB gets a lot of secondary testing downstream, and exposure to Apple's
own developer community.
### FreeBSD
FreeBSD is an example of quite self-contained support, managed by the project's
community.
* FreeBSD is open source and available to anyone to build, modify, run on
hardware, emulate or virtualize.
* FreeBSD support is mostly in the native target parts of `lldb-server`,
so issues with it rarely impact any other target.
* It is quite similar to Linux, so it does not cause large scale changes to
fundamental assumptions LLDB makes.
* It has at least one maintainer and sometimes contributions to upstream LLDB
from the FreeBSD community.
* Issues are solved by the maintainer and the FreeBSD community.
* LLDB on FreeBSD is tested each time FreeBSD updates the version used in the
base system, which is roughly per upstream LLVM release.
### Linux
Linux is an example where the Linux community as a whole does not do all the
work of Linux support. Some architectures have a wide contributor base and
others have company-specific contributors as well.
* Linux is open source, freely available, and can be installed on hardware,
virtualised, emulated, and so on. Most problems can be reproduced in more than
one way.
* Many architectures are tested by the architecture vendor, others
are tested on community-provided hardware.
* LLDB is a commonly available Linux package, so further testing happens during
various Linux distributions' release cycles.
* Support for a new architecture is quite easy to isolate, so problems do not
impact other Linux architectures, or other operating systems.
* Linux is very popular, therefore its choice of standards informs many core
features of LLDB.
* It has more than one listed maintainer and many contributors.
Issues are resolved by the community.
### MSP430
MSP430 is handled as a bare-metal (no operating system) target in LLDB. So it
is the most minimal example.
* LLVM already has MSP430 support, so changes to enable it in LLDB were minimal.
* It does not use `lldb-server`, and `lldb` only required minor adjustments to
be compatible with the commonly used debug server.
* An ABI plugin was added, which is isolated to MSP430 only.
* It is not systematically tested anywhere, but gets some use by MSP430
developers.
* It has no documented maintainer.
* We rely on users to report issues with it, and would likely guide them to fix
them themselves.
## Components Of Target Support
This is a very high-level view. We recommend you combine this with reading the
changes made for recently added targets, as each target is going to be slightly
different.
Assuming that:
* You want to support a combination of a new architecture and a new operating
system.
* LLVM already supports your architecture and operating system.
* You want to port most of the LLDB features, which means porting both `lldb`
and `lldb-server`.
Then this is a list of components you will need to write. If your target is
similar to others you may be able to reuse existing components and we encourage
you to do so.
First the components concerned with operating systems:
* A `Platform` plugin. For example `PlatformLinux`.
* A `HostInfo` plugin. For example `HostInfoLinux`.
* A native process plugin (native means it runs on your target). For example
`NativeProcessLinux`.
* Signal information. For example `LinuxSignals`.
* A dynamic loader plugin. For example `DynamicLoaderPOSIXDYLD`.
* An object file plugin. For example `ObjectFileELF`.
Then the components concerned with architecture (though the distinction is often
inexact):
* ABI plugin. For example `ABISysV_arm64`.
* Architecture plugin. For example `ArchitectureAArch64`.
* Register definitions and register context (a context is a collection of
registers). For example `NativeRegisterContextLinux_arm64`.
* Unwinding support for backtracing. LLDB needs to be taught about any
architecture-specific directives.
* Instruction emulation. For example `EmulateInstructionARM64`. Most targets
need to emulate a small number of instructions for unwinding. If your target
does not have hardware single-step, you will need to emulate many times more
than that.
The order of implementation will vary in each case. Each component does not
need to be fully implemented for you to start work on the next.
If you are going to be running `lldb` on your target, then an obvious first step
is to get `lldb` to build there without any changes. This `lldb` will only be
useful for remotely debugging other targets, but you will at least know that
the build system is compatible.
Next, try `lldb` with any existing debug servers for your target. If they
are similar to `lldb-server` or `gdbserver`, this can flush out some
obvious issues in `lldb`.
Then begin porting `lldb-server` to your target. We recommend that you
get the test suite to run as soon as possible, however bad the results
are.
As you have seen above, there are a lot of moving parts to a debugger. So
having some set of results to measure progress is very important.
Sometimes a change will get one test to pass, sometimes hundreds, and it
is easy to regress if you are not careful.
## Target Hardware Features
Some hardware features can make porting easier or harder. Below is a
non-exhaustive list of features and their impacts on porting LLDB.
### Hardware Single Step
If the target lacks this feature, you will have to implement software single
stepping. This is much more complex and involves emulating any instruction that
could modify the program counter.
### Instruction Bundles and Sequences
This is any situation where to resume the program you have to replay some
previous instructions. LLDB needs to know the extent of the sequence.
For example, an atomic sequence may implement an atomic operation by looping
until success. When stepping through the sequence normally, this check will
always fail (due to the debug exceptions) and cause it to loop forever.
You can teach LLDB to find the sequence start point, and replay the whole thing
as if it were one step.
If you have instruction bundles, check how breakpoints behave and where in the
bundles they can be placed.
### Single Instruction Equivalents of Sequences
The opposite of the previous point. If your target has single instructions
for what is normally a sequence, this reduces the work needed in LLDB. Single
instruction atomics are a common example.
### Runtime Register Resizing
Anything like AArch64's Scalable Vector Extension (SVE) registers. At each stop
event the registers may have a different size.
Support for this is currently SVE specific as it requires `lldb` to know which
registers scale and what to derive their size from.
### Registers That Come And Go At Runtime
If you have registers that are not present for the entire program runtime you
will need to decide how to present that.
The one example we support right now is AArch64's Scalable Matrix Extension
(SME) `ZA` register. This register can be switched off when not in use.
We handle this by showing a fake zero value at these times, with a separate
mode bit in another register so users can tell a real zero from a fake zero.
The more fundamental and the more numerous the registers are, the more
likely you are to confuse users by showing them even when they are unusable.
For instance if you have two execution modes that use separate register sets,
showing both all the time may be confusing for users.
### Registers With The Same Name In Different Contexts
When you have banked registers or a copy of a register for each execution mode,
it usually only has one name. You need to decide if it makes sense to
allow users to access each one separately.
For example, AArch64's SME extension adds a "streaming mode". SVE registers
exist in the normal mode and the streaming mode. However programs only ever use
one or the other, and the values are cleared when the mode is switched. So there
is no reason to let users write to the inactive mode's registers and we just
show 1 set with the normal naming. That set always refers to the active mode.
However if you have overlapping sets that can hold their own values, you may
want to make the normal register name the active set, and have a way to address
the other sets.
### Execution Modes That Change Instruction Encoding
Arm (meaning Armv7 and prior) has 2 execution modes: Arm and Thumb. If you
attempt to execute Thumb mode code in Arm mode, it will not work. Programs can
mix the two modes by using special mode switching branches.
The debugger has to be aware of what mode the inferior is in so that it can
correctly compare addresses, place breakpoints and use the correct breakpoint
instruction encoding.
In Arm's case, the information comes from markers in the program file, and the
bottom bit of the program counter. This is often a source of bugs because many
parts of the debugger have to know that this bit is not part of the instruction
address.
### Variable Length Instruction Encoding
LLDB already supports a wide variety of encoding strategies, so variable length
encoding is not much more work than fixed length.
Of the current targets we have:
* Intel which is variable length.
* Arm (Armv7 and prior) with Arm (32-bit), Thumb 1 (16-bit) and Thumb 2
(a mix of 16 and 32-bit).
* RISC-V which is variable, usually 32-bit or the 16-bit compressed
instructions.
* AArch64 which is fixed length, always 32-bit.
### Hardware Breakpoints and Watchpoints
Code breakpoints can be implemented in software by replacing an instruction with
a software breakpoint instruction.
However, if you want to only stop in certain situations (a single address space,
a single execution mode, and so on), code breakpoints implemented in hardware
will be much faster.
Doing it in software means you have to context switch between the debug stub and
the debugger to filter every stop event. Which is slow even when locally
debugging. Put the debug server on the end of a high latency connection and the
slow down is multiplied.
In addition, hardware code breakpoints can be set in read-only memory. Which is
important for code executing out of ROM, which is common on embedded targets.
Watchpoints are used to wait for a specific type of access to a specific range
of memory. Doing this in software is possible but very invasive so use hardware
watchpoints if you can.
For comparison, one way to implement a software watchpoint is to unmap the
memory around a location and then filter the memory faults to find the access
you are looking for. This requires a lot of traffic between debugger and debug
server, and needs to be implemented for every supported operating system.
Whereas a hardware watchpoint is usually a few registers programmed with
hardware specific values, and most operating systems expose those registers
directly to userspace.
:::{note}
Hardware often has many more features than LLDB makes use of. What we make use
of is decided by how useful it will be to how many users and how understandable
it will be when presented in the LLDB interface.
:::
### Accurate Breakpoints and Watchpoints
Ideally your break and watchpoint exceptions contain enough information to
know exactly what caused them. Which seems obvious from a software level, but
if you check the architecture specifications you will find that they often allow
a range of behaviours.
For instance, if you watch a 4 byte chunk of memory and an instruction writes 8
bytes over it, is the hardware required to report an address in the watched 4
bytes? Or can it report an address in the second 4 bytes, because it is still
part of the write that triggered the watchpoint?
LLDB will assume accuracy unless told otherwise, and if there are untraceable
situations users will have to figure out which watch or breakpoints to manually
disable so that they can continue.
### Addresses That Are More Than Just Numbers
Though many programming languages have rules that prevent the use of pointers
in some integer-like ways, the reality is that a lot of hardware treats them
as numbers.
This changes with capabilities (for example CHERI), mode bits (Arm's Thumb) and
re-use of non-address bits (AArch64's TBI, MTE and PAC). A pointer is no longer
just a number that refers to a memory location, it contains extra information.
Where the size of the pointer is equal to the bit width of the architecture
(64-bit pointers on AArch64 for example), LLDB can probably handle it. LLDB
already has the concept of "non-address bits" that must be removed to get the
memory address from a pointer.
If pointers are like capabilities where their size is greater than that of
a memory address, you will need to change the type LLDB uses to store addresses
which is a lot more work.