GetBase determines the base address to subtract from virtual address to get symbol table address. For an executable, the base is 0. Otherwise, it's a shared library, and the base is the address where the mapping starts. The kernel needs special handling.
(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64)
| 214 | // is 0. Otherwise, it's a shared library, and the base is the |
| 215 | // address where the mapping starts. The kernel needs special handling. |
| 216 | func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, error) { |
| 217 | |
| 218 | if start == 0 && offset == 0 && (limit == ^uint64(0) || limit == 0) { |
| 219 | // Some tools may introduce a fake mapping that spans the entire |
| 220 | // address space. Assume that the address has already been |
| 221 | // adjusted, so no additional base adjustment is necessary. |
| 222 | return 0, nil |
| 223 | } |
| 224 | |
| 225 | switch fh.Type { |
| 226 | case elf.ET_EXEC: |
| 227 | if loadSegment == nil { |
| 228 | // Assume fixed-address executable and so no adjustment. |
| 229 | return 0, nil |
| 230 | } |
| 231 | if stextOffset == nil && start > 0 && start < 0x8000000000000000 { |
| 232 | // A regular user-mode executable. Compute the base offset using same |
| 233 | // arithmetic as in ET_DYN case below, see the explanation there. |
| 234 | // Ideally, the condition would just be "stextOffset == nil" as that |
| 235 | // represents the address of _stext symbol in the vmlinux image. Alas, |
| 236 | // the caller may skip reading it from the binary (it's expensive to scan |
| 237 | // all the symbols) and so it may be nil even for the kernel executable. |
| 238 | // So additionally check that the start is within the user-mode half of |
| 239 | // the 64-bit address space. |
| 240 | return start - offset + loadSegment.Off - loadSegment.Vaddr, nil |
| 241 | } |
| 242 | // Various kernel heuristics and cases are handled separately. |
| 243 | if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match { |
| 244 | return base, nil |
| 245 | } |
| 246 | // ChromeOS can remap its kernel to 0, and the caller might have not found |
| 247 | // the _stext symbol. Split this case from kernelBase() above, since we don't |
| 248 | // want to apply it to an ET_DYN user-mode executable. |
| 249 | if start == 0 && limit != 0 && stextOffset == nil { |
| 250 | return start - loadSegment.Vaddr, nil |
| 251 | } |
| 252 | |
| 253 | return 0, fmt.Errorf("don't know how to handle EXEC segment: %v start=0x%x limit=0x%x offset=0x%x", *loadSegment, start, limit, offset) |
| 254 | case elf.ET_REL: |
| 255 | if offset != 0 { |
| 256 | return 0, fmt.Errorf("don't know how to handle mapping.Offset") |
| 257 | } |
| 258 | return start, nil |
| 259 | case elf.ET_DYN: |
| 260 | // The process mapping information, start = start of virtual address range, |
| 261 | // and offset = offset in the executable file of the start address, tells us |
| 262 | // that a runtime virtual address x maps to a file offset |
| 263 | // fx = x - start + offset. |
| 264 | if loadSegment == nil { |
| 265 | return start - offset, nil |
| 266 | } |
| 267 | // Kernels compiled as PIE can be ET_DYN as well. Use heuristic, similar to |
| 268 | // the ET_EXEC case above. |
| 269 | if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match { |
| 270 | return base, nil |
| 271 | } |
| 272 | // The program header, if not nil, indicates the offset in the file where |
| 273 | // the executable segment is located (loadSegment.Off), and the base virtual |
searching dependent graphs…