|
8 | 8 |
|
9 | 9 | #include "llvm/MC/MCSFrame.h" |
10 | 10 | #include "llvm/BinaryFormat/SFrame.h" |
| 11 | +#include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" |
| 12 | +#include "llvm/DebugInfo/DWARF/LowLevel/DWARFDataExtractorSimple.h" |
11 | 13 | #include "llvm/MC/MCAsmInfo.h" |
12 | 14 | #include "llvm/MC/MCContext.h" |
13 | 15 | #include "llvm/MC/MCObjectFileInfo.h" |
@@ -211,8 +213,152 @@ class SFrameEmitterImpl { |
211 | 213 | return true; |
212 | 214 | } |
213 | 215 |
|
| 216 | + // Technically, the escape data could be anything, but it is commonly a dwarf |
| 217 | + // CFI program. Even then, it could contain an arbitrarily complicated Dwarf |
| 218 | + // expression. Following gnu-gas, look for certain common cases that could |
| 219 | + // invalidate an FDE, emit a warning for those sequences, and don't generate |
| 220 | + // an FDE in those cases. Allow any that are known safe. It is likely that |
| 221 | + // more thorough test cases could refine this code, but it handles the most |
| 222 | + // important ones compatibly with gas. |
| 223 | + // Returns true if the CFI escape sequence is safe for sframes. |
| 224 | + bool isCFIEscapeSafe(SFrameFDE &FDE, const SFrameFRE &FRE, |
| 225 | + const MCCFIInstruction &CFI) { |
| 226 | + const MCAsmInfo *AI = Streamer.getContext().getAsmInfo(); |
| 227 | + DWARFDataExtractorSimple data(CFI.getValues(), AI->isLittleEndian(), |
| 228 | + AI->getCodePointerSize()); |
| 229 | + |
| 230 | + // Normally, both alignment factors are extracted from the enclosing Dwarf |
| 231 | + // FDE or CIE. We don't have one here. Alignments are used for scaling |
| 232 | + // factors for ops like CFA_def_cfa_offset_sf. But this particular function |
| 233 | + // is only interested in registers. |
| 234 | + dwarf::CFIProgram P(/*CodeAlignmentFactor=*/1, |
| 235 | + /*DataAlignmentFactor=*/1, |
| 236 | + Streamer.getContext().getTargetTriple().getArch()); |
| 237 | + uint64_t Offset = 0; |
| 238 | + if (P.parse(data, &Offset, CFI.getValues().size())) { |
| 239 | + // Not a parsable dwarf expression. Assume the worst. |
| 240 | + Streamer.getContext().reportWarning( |
| 241 | + CFI.getLoc(), |
| 242 | + "skipping SFrame FDE; .cfi_escape with unknown effects"); |
| 243 | + return false; |
| 244 | + } |
| 245 | + |
| 246 | + // This loop deals with dwarf::CFIProgram::Instructions. Everywhere else |
| 247 | + // this file deals with MCCFIInstructions. |
| 248 | + for (const dwarf::CFIProgram::Instruction &I : P) { |
| 249 | + switch (I.Opcode) { |
| 250 | + case dwarf::DW_CFA_nop: |
| 251 | + break; |
| 252 | + case dwarf::DW_CFA_val_offset: { |
| 253 | + // First argument is a register. Anything that touches CFA, FP, or RA is |
| 254 | + // a problem, but allow others through. As an even more special case, |
| 255 | + // allow SP + 0. |
| 256 | + auto Reg = I.getOperandAsUnsigned(P, 0); |
| 257 | + // The parser should have failed in this case. |
| 258 | + assert(Reg && "DW_CFA_val_offset with no register."); |
| 259 | + bool SPOk = true; |
| 260 | + if (*Reg == SPReg) { |
| 261 | + auto Opnd = I.getOperandAsSigned(P, 1); |
| 262 | + if (!Opnd || *Opnd != 0) |
| 263 | + SPOk = false; |
| 264 | + } |
| 265 | + if (!SPOk || *Reg == RAReg || *Reg == FPReg) { |
| 266 | + StringRef RN = *Reg == SPReg |
| 267 | + ? "SP reg " |
| 268 | + : (*Reg == FPReg ? "FP reg " : "RA reg "); |
| 269 | + Streamer.getContext().reportWarning( |
| 270 | + CFI.getLoc(), |
| 271 | + Twine( |
| 272 | + "skipping SFrame FDE; .cfi_escape DW_CFA_val_offset with ") + |
| 273 | + RN + Twine(*Reg)); |
| 274 | + return false; |
| 275 | + } |
| 276 | + } break; |
| 277 | + case dwarf::DW_CFA_expression: { |
| 278 | + // First argument is a register. Anything that touches CFA, FP, or RA is |
| 279 | + // a problem, but allow others through. |
| 280 | + auto Reg = I.getOperandAsUnsigned(P, 0); |
| 281 | + if (!Reg) { |
| 282 | + Streamer.getContext().reportWarning( |
| 283 | + CFI.getLoc(), |
| 284 | + "skipping SFrame FDE; .cfi_escape with unknown effects"); |
| 285 | + return false; |
| 286 | + } |
| 287 | + if (*Reg == SPReg || *Reg == RAReg || *Reg == FPReg) { |
| 288 | + StringRef RN = *Reg == SPReg |
| 289 | + ? "SP reg " |
| 290 | + : (*Reg == FPReg ? "FP reg " : "RA reg "); |
| 291 | + Streamer.getContext().reportWarning( |
| 292 | + CFI.getLoc(), |
| 293 | + Twine( |
| 294 | + "skipping SFrame FDE; .cfi_escape DW_CFA_expression with ") + |
| 295 | + RN + Twine(*Reg)); |
| 296 | + return false; |
| 297 | + } |
| 298 | + } break; |
| 299 | + case dwarf::DW_CFA_GNU_args_size: { |
| 300 | + auto Size = I.getOperandAsSigned(P, 0); |
| 301 | + // Zero size doesn't affect the cfa. |
| 302 | + if (Size && *Size == 0) |
| 303 | + break; |
| 304 | + if (FRE.Info.getBaseRegister() != BaseReg::FP) { |
| 305 | + Streamer.getContext().reportWarning( |
| 306 | + CFI.getLoc(), |
| 307 | + Twine("skipping SFrame FDE; .cfi_escape DW_CFA_GNU_args_size " |
| 308 | + "with non frame-pointer CFA")); |
| 309 | + return false; |
| 310 | + } |
| 311 | + } break; |
| 312 | + // Cases that gas doesn't specially handle. TODO: Some of these could be |
| 313 | + // analyzed and handled instead of just punting. But these are uncommon, |
| 314 | + // or should be written as normal cfi directives. Some will need fixes to |
| 315 | + // the scaling factor. |
| 316 | + case dwarf::DW_CFA_advance_loc: |
| 317 | + case dwarf::DW_CFA_offset: |
| 318 | + case dwarf::DW_CFA_restore: |
| 319 | + case dwarf::DW_CFA_set_loc: |
| 320 | + case dwarf::DW_CFA_advance_loc1: |
| 321 | + case dwarf::DW_CFA_advance_loc2: |
| 322 | + case dwarf::DW_CFA_advance_loc4: |
| 323 | + case dwarf::DW_CFA_offset_extended: |
| 324 | + case dwarf::DW_CFA_restore_extended: |
| 325 | + case dwarf::DW_CFA_undefined: |
| 326 | + case dwarf::DW_CFA_same_value: |
| 327 | + case dwarf::DW_CFA_register: |
| 328 | + case dwarf::DW_CFA_remember_state: |
| 329 | + case dwarf::DW_CFA_restore_state: |
| 330 | + case dwarf::DW_CFA_def_cfa: |
| 331 | + case dwarf::DW_CFA_def_cfa_register: |
| 332 | + case dwarf::DW_CFA_def_cfa_offset: |
| 333 | + case dwarf::DW_CFA_def_cfa_expression: |
| 334 | + case dwarf::DW_CFA_offset_extended_sf: |
| 335 | + case dwarf::DW_CFA_def_cfa_sf: |
| 336 | + case dwarf::DW_CFA_def_cfa_offset_sf: |
| 337 | + case dwarf::DW_CFA_val_offset_sf: |
| 338 | + case dwarf::DW_CFA_val_expression: |
| 339 | + case dwarf::DW_CFA_MIPS_advance_loc8: |
| 340 | + case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: |
| 341 | + case dwarf::DW_CFA_AARCH64_negate_ra_state: |
| 342 | + case dwarf::DW_CFA_LLVM_def_aspace_cfa: |
| 343 | + case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: |
| 344 | + Streamer.getContext().reportWarning( |
| 345 | + CFI.getLoc(), "skipping SFrame FDE; .cfi_escape " |
| 346 | + "CFA expression with unknown side effects"); |
| 347 | + return false; |
| 348 | + default: |
| 349 | + // Dwarf expression was only partially valid, and user could have |
| 350 | + // written anything. |
| 351 | + Streamer.getContext().reportWarning( |
| 352 | + CFI.getLoc(), |
| 353 | + "skipping SFrame FDE; .cfi_escape with unknown effects"); |
| 354 | + return false; |
| 355 | + } |
| 356 | + } |
| 357 | + return true; |
| 358 | + } |
| 359 | + |
214 | 360 | // Add the effects of CFI to the current FDE, creating a new FRE when |
215 | | - // necessary. |
| 361 | + // necessary. Return true if the CFI is representable in the sframe format. |
216 | 362 | bool handleCFI(SFrameFDE &FDE, SFrameFRE &FRE, const MCCFIInstruction &CFI) { |
217 | 363 | switch (CFI.getOperation()) { |
218 | 364 | case MCCFIInstruction::OpDefCfaRegister: |
@@ -265,10 +411,11 @@ class SFrameEmitterImpl { |
265 | 411 | FRE = FDE.SaveState.pop_back_val(); |
266 | 412 | return true; |
267 | 413 | case MCCFIInstruction::OpEscape: |
268 | | - // TODO: Implement. Will use FDE. |
269 | | - return true; |
| 414 | + // This is a string of bytes that contains an arbitrary dwarf-expression |
| 415 | + // that may or may not affect unwind info. |
| 416 | + return isCFIEscapeSafe(FDE, FRE, CFI); |
270 | 417 | default: |
271 | | - // Instructions that don't affect the CFA, RA, and SP can be safely |
| 418 | + // Instructions that don't affect the CFA, RA, and FP can be safely |
272 | 419 | // ignored. |
273 | 420 | return true; |
274 | 421 | } |
|
0 commit comments